[Angular] ngx-translate를 활용한 다국어 처리 구현 가이드

Adam Kim·2025년 1월 29일
0

angular

목록 보기
3/11
post-thumbnail

이 가이드에서는 Angular 애플리케이션에서 ngx-translate를 사용하여 다국어 처리를 구현하는 방법을 다룹니다. NgModule 방식과 Standalone 방식을 모두 지원하며, 엔터프라이즈급 애플리케이션을 위한 고급 설정 옵션과 모범 사례를 설명합니다.

설치

필요한 의존성 패키지 설치:

npm install @ngx-translate/core @ngx-translate/http-loader --save

구현 방식

1. NgModule 기반 구현

Core 모듈 설정

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';

export function HttpLoaderFactory(httpClient: HttpClient) {
  return new TranslateHttpLoader(httpClient, 'assets/i18n/', '.json');
}

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient]
      },
      defaultLanguage: 'ko-KR',
      useDefaultLang: true
    })
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

2. Standalone 구현

부트스트랩 설정

// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { HttpClient, provideHttpClient } from '@angular/common/http';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { importProvidersFrom } from '@angular/core';

export function HttpLoaderFactory(httpClient: HttpClient) {
  return new TranslateHttpLoader(httpClient, 'assets/i18n/', '.json');
}

bootstrapApplication(AppComponent, {
  providers: [
    provideHttpClient(),
    importProvidersFrom(
      TranslateModule.forRoot({
        loader: {
          provide: TranslateLoader,
          useFactory: HttpLoaderFactory,
          deps: [HttpClient]
        },
        defaultLanguage: 'ko-KR',
        useDefaultLang: true
      })
    )
  ]
}).catch(err => console.error(err));

Standalone 컴포넌트 구현

// app.component.ts
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { LanguageService } from './services/language.service';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    CommonModule,
    TranslateModule
  ],
  providers: [LanguageService],
  template: `
    <div [innerHTML]="'common.welcome' | translate"></div>
  `
})
export class AppComponent {
  // standalone은 이 코드를 사용
  languageService = inject(LanguageService);
  
  // module은 이 코드를 사용
  constructor(private languageService: LanguageService)  {
    this.languageService.initialize();
  }
}

타입 안전성이 보장된 언어 설정

언어 인터페이스

// interfaces/language.interface.ts
export enum LanguageCode {
  'en-US' = 'English',
  'ko-KR' = '한국어',
  'zh-CN' = '简体中文',
  'ja-JP' = '日本語'
}

export interface TranslationKeys {
  common: {
    welcome: string;
    login: string;
    logout: string;
  };
  errors: {
    required: string;
    invalid: string;
  };
}

언어 서비스 구현

// services/language.service.ts
import { Injectable, inject } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { LanguageCode } from '../interfaces/language.interface';
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class LanguageService {
  // standalone은 이 코드를 사용
  languageService = inject(LanguageService);
  
  private currentLang = new BehaviorSubject<string>(null);
  currentLang$ = this.currentLang.asObservable();
  
  // module은 이 코드를 사용
  constructor(private translateService: TranslateService) {}

  initialize(): void {
    const languages = Object.keys(LanguageCode);
    this.translateService.addLangs(languages);
    
    const browserLang = this.translateService.getBrowserLang();
    const defaultLang = languages.find(lang => 
      browserLang.toLowerCase() === lang.toLowerCase()
    ) || 'ko-KR';
    
    this.translateService.setDefaultLang(defaultLang);
    this.setLanguage(defaultLang);
  }

  setLanguage(lang: keyof typeof LanguageCode): void {
    this.translateService.use(lang);
    this.currentLang.next(lang);
  }

  getCurrentLang(): Observable<string> {
    return this.currentLang$;
  }
}

번역 파일 구조

// assets/i18n/ko-KR.json
{
  "common": {
    "welcome": "애플리케이션에 오신 것을 환영합니다",
    "login": "로그인",
    "logout": "로그아웃"
  },
  "errors": {
    "required": "필수 입력 항목입니다",
    "invalid": "잘못된 입력값입니다"
  }
}

고급 사용 예시

1. 파라미터를 포함한 동적 번역

// 템플릿 사용 예시
@Component({
  template: `
    <div [innerHTML]="'notifications.welcome' | translate:{ username: currentUser.name }"></div>
  `
})

// 번역 파일
{
  "notifications": {
    "welcome": "{{username}}님, 환영합니다!"
  }
}

2. 언어 선택기 구현

@Component({
  selector: 'app-language-switcher',
  standalone: true,
  imports: [CommonModule, TranslateModule],
  template: `
    <select (change)="onLanguageChange($event)" [value]="currentLang$ | async">
      <option *ngFor="let lang of languageCodes" [value]="lang">
        {{ LanguageCode[lang] }}
      </option>
    </select>
  `
})
export class LanguageSwitcherComponent {
  // standalone은 이 코드를 사용
  languageService = inject(LanguageService);
  
  protected readonly LanguageCode = LanguageCode;
  protected readonly languageCodes = Object.keys(LanguageCode);
  protected currentLang$ = this.languageService.getCurrentLang();

  // module은 이 코드를 사용
  constructor(private languageService: LanguageService) {}

  onLanguageChange(event: Event): void {
    const lang = (event.target as HTMLSelectElement).value as keyof typeof LanguageCode;
    this.languageService.setLanguage(lang);
  }
}

모범 사례 및 고려사항

  1. 성능 최적화

    • 대규모 애플리케이션의 경우 번역 파일 지연 로딩 사용
    • 로드된 번역에 대한 캐싱 전략 구현
    • 프로덕션 빌드시 번역 컴파일 고려
  2. 타입 안전성

    • 번역 키에 대한 강력한 타입 정의
    • 번역 구조에 TypeScript 인터페이스 사용
    • 빌드 과정에서 번역 파일 유효성 검사 구현
  3. 유지보수

    • 번역을 계층 구조로 구성
    • 번역 키 관리 시스템 구현
    • 대규모 프로젝트의 경우 번역 관리 도구 사용
  4. HTML 콘텐츠 처리

    • HTML을 포함한 번역은 항상 [innerHTML] 사용
    • 사용자 제공 번역 콘텐츠에 대한 보안 처리 구현
    • HTML 번역 사용시 접근성 고려

일반적인 문제점과 해결방안

  1. 중첩된 HTML 요소

    // 잘못된 방법
    <div>{{ 'key.with.html' | translate }}<span>추가 내용</span></div>
    
    // 올바른 방법
    <div [innerHTML]="'key.with.html' | translate"></div>
    <span>추가 내용</span>
  2. 비동기 번역 로딩

    // 번역 로딩 상태 처리
    this.translateService.get('key').subscribe({
      next: (translation: string) => {
        // 번역 성공 처리
      },
      error: (err) => {
        console.error('번역 오류:', err);
        // 대체 처리
      }
    });

Angular 기능과의 통합

  1. 라우트 가드

    @Injectable({ providedIn: 'root' })
    export class TranslationLoadGuard implements CanActivate {
      // standalone은 이 코드를 사용
      translateService = inject(TranslateService);
      
      // module은 이 코드를 사용
      constructor(private translateService: TranslateService) {}
    
      canActivate(): Observable<boolean> {
        return this.translateService.get('common.welcome')
          .pipe(map(() => true));
      }
    }
  2. 에러 처리

    @Injectable({ providedIn: 'root' })
    export class GlobalErrorHandler implements ErrorHandler {
      // standalone은 이 코드를 사용
      translateService = inject(TranslateService);
      
      // 모듈은 이 코드를 사용
      constructor(private translateService: TranslateService) {}
    
      handleError(error: Error): void {
        this.translateService.get('errors.unexpected')
          .subscribe(message => {
            // 번역된 메시지로 에러 처리
          });
      }
    }

참고 사이트

profile
Angular2+ Developer

0개의 댓글