국제화(i18n)는 글로벌 Angular 애플리케이션을 구축하는 데 필수적입니다. 이 가이드는 적절한 리소스 로딩, 초기화 및 사용 패턴으로 Angular에서 i18next를 구현하는 방법을 보여줍니다.
i18next는 강력한 기능과 유연성을 제공하는 가장 인기 있는 i18n 프레임워크 중 하나입니다:
npm 또는 yarn을 사용하여 i18next를 설치하세요:
npm install i18next그게 전부입니다! i18next는 프레임워크에 구애받지 않는 독립형 라이브러리입니다. angular-i18next 또는 다른 래퍼가 필요하지 않습니다 - Angular 애플리케이션에서 i18next를 직접 사용하세요.
Angular에서 i18next를 사용하여 번역 리소스를 로딩하는 두 가지 주요 접근 방식이 있습니다:
동적 임포트를 사용하면 Webpack의 코드 분할을 활용하여 필요 시 번역을 로드합니다. 이 접근 방식은 많은 언어 또는 큰 번역 파일을 가진 애플리케이션에 이상적입니다.
장점:
예시: 동적 임포트를 통한 로딩
private async loadLanguageResources(
lang: string,
): Promise<Record<string, Record<string, unknown>>> {
const languageResources: Record<string, Record<string, unknown>> = {};
for (const namespace of NAMESPACES) {
const translationModule = await this.loadJson(lang, namespace);
languageResources[namespace] = translationModule.default;
}
return languageResources;
}
private async loadJson(
language: string,
namespace: string,
): Promise<{ default: Record<string, unknown> }> {
// Webpack automatically code-splits this into separate chunks
return import(`../../i18n/${language}/${namespace}.json`);
}이 접근 방식은 번역 파일을 ES 모듈로 가져와 Webpack이 이를 자동으로 코드 분할하여 필요 시 로드할 수 있도록 합니다.
HTTP 백엔드 플러그인은 서버 또는 CDN에서 HTTP 요청을 통해 번역을 로드합니다. 이는 애플리케이션을 다시 빌드하지 않고 번역을 업데이트하려는 경우 유용합니다.
장점:
단점:
예시: HTTP 백엔드를 통한 로딩
import HttpBackend from 'i18next-http-backend';
await init({
lng: initialLanguage,
fallbackLng: DEFAULT_LANGUAGE,
backend: {
loadPath: '/assets/i18n/{{lng}}/{{ns}}.json',
},
use: [HttpBackend],
ns: NAMESPACES,
defaultNS: DEFAULT_NAMESPACE,
});이 접근 방식은 런타임에 서버에서 번역 파일을 가져옵니다. SSR을 위한 추가 구성이 필요합니다.
i18next를 올바르게 초기화하는 것은 런타임 오류를 피하고 애플리케이션이 렌더링될 때 번역이 사용 가능하도록 하는 데 중요합니다.
Angular의 provideAppInitializer 함수(Angular 18에서 도입됨)는 애플리케이션이 렌더링되기 전에 i18next가 완전히 초기화되도록 보장합니다. 제공된 함수는 앱 부트스트랩 중에 실행되며, 초기화는 Promise가 해결될 때까지 완료되지 않습니다. 이는 번역 키가 번역된 텍스트 대신 나타나는 것을 방지합니다.
예시: 앱 초기화기 구성
export const appConfig: ApplicationConfig = {
providers: [
// ... other providers
provideAppInitializer(initializeI18n),
// ... more providers
],
};이것이 중요한 이유:
초기화 함수는 리소스를 로드하고 i18next 설정을 구성하는 역할을 합니다.
예시: i18next 초기화
public async initializeI18n(): Promise<void> {
const initialLanguage = this.getInitialLanguage();
const resources: Record<string, Record<string, Record<string, unknown>>> = {};
if (isPlatformBrowser(this.platformId)) {
// Browser: Only load the specific language needed
const languageResources = await this.loadLanguageResources(initialLanguage);
resources[initialLanguage] = languageResources;
} else {
// SSR: Load all languages for prerendering
for (const lang of SUPPORTED_LANGUAGES) {
const languageResources = await this.loadLanguageResources(lang);
resources[lang] = languageResources;
}
}
await init({
lng: initialLanguage,
fallbackLng: DEFAULT_LANGUAGE,
resources,
ns: NAMESPACES,
defaultNS: DEFAULT_NAMESPACE,
interpolation: {
escapeValue: false,
},
});
}핵심 사항:
Angular 번역 파이프는 템플릿에서 번역을 사용하는 깔끔한 방법을 제공합니다.
예시: 번역 파이프
import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';
@Pipe({
name: 't',
standalone: true,
pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
transform(key: string, options?: Record<string, unknown>): string {
return t(key, options);
}
}파이프는 언어가 변경될 때 업데이트되도록 순수: false로 표시됩니다. 이는 언어 변경이 번역 키 자체를 수정하지 않기 때문에 중요합니다.
템플릿에서:
<!-- With namespace -->
<p>{{ "common:welcomeMessage" | t }}</p>
<!-- With nested keys -->
<p>{{ "dashboard.title" | t }}</p>
<!-- Simple interpolation -->
<p>{{ "greeting" | t: { name: userName } }}</p>
<!-- Result: "Hello, John!" from greeting: "Hello, {{name}}!" -->
<!-- With Angular pipes -->
<p>{{ "price" | t: { amount: 29.99 | currency } }}</p>
<!-- Result: "Price: $29.99" from: "Price: {{amount}}" -->
<!-- Multiple variables -->
<p>{{ "updated" | t: { date: lastModified | date, user: currentUser } }}</p>
<!-- Result: "Updated on Jan 15, 2024 by Alice" from: "Updated on {{date}} by {{user}}." -->
보간(interpolation)을 통해 번역에 동적 값을 삽입할 수 있습니다. 변수를 전달하고, 형식을 지정하기 위해 Angular 파이프를 사용하며, 여러 값을 단일 번역 문자열에 결합할 수 있습니다.
컴포넌트 코드에서:
import { Component } from '@angular/core';
import { t } from 'i18next';
@Component({
selector: 'app-dashboard',
template: `
<h1>{{ title }}</h1>
<p>{{ welcomeMsg }}</p>
`,
})
export class DashboardComponent {
title = t('dashboard.title');
welcomeMsg = t('greeting', { name: 'John' });
}템플릿 사용을 위해 파이프 구문은 깔끔하고 읽기 쉽습니다. 프로그래밍적 접근을 위해서는 t()를 직접 호출합니다. 이는 알림 표시, 동적 제목 설정 또는 컴포넌트 로직에서 번역 처리에 유용합니다.
앱 구조를 반영하는 점 표기법을 사용하세요: 'auth:login.title' 대신 'loginTitle' 또는 원시 텍스트를 사용합니다.
번역을 논리적인 네임스페이스(예: common.json, auth.json, dashboard.json)로 나누어 유지 관리를 개선하세요.
모든 언어에서 단수/복수 형태를 올바르게 처리하기 위해 i18next의 내장 복수형 지원을 사용하세요.
앱 부트스트랩 중에 provideAppInitializer()를 사용하여 앱을 렌더링하기 전에 항상 i18next가 초기화되었는지 확인하세요.
TypeScript를 사용하여 JSON 파일에서 직접 번역 키를 추출하여 완전한 타입 안전성을 확보하세요. 타입 정의를 수동으로 유지하는 대신 TypeScript의 typeof와 동적 가져오기를 활용하여 실제 번역 파일에서 자동으로 타입을 생성하세요.
재귀적인 NestedKeys 헬퍼 타입을 사용하여 JSON 번역 파일에서 가능한 모든 키를 추출하는 타입을 정의하세요.
import common from './en/common.json'; // defaultNS: DEFAULT_NAMESPACE,
import auth from './en/auth.json'; // namespaces
type NestedKeys<T, Prefix extends string = ''> = T extends object
? {
[K in keyof T & (string | number)]: T[K] extends object
?
| NestedKeys<T[K], `${Prefix}${K & (string | number)}.`>
| `${Prefix}${K & (string | number)}`
: `${Prefix}${K & (string | number)}`;
}[keyof T & (string | number)]
: never;
export type TranslationKey =
| `common:${NestedKeys<typeof common>}`
| `auth:${NestedKeys<typeof auth>}`;파이프의 변환 메서드를 문자열 대신 TranslationKey를 수용하도록 변경하세요. 이는 템플릿에서 타입 안전성을 제공하고 유효한 번역 키만 사용할 수 있도록 보장합니다.
import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';
import { TranslationKey } from '../../i18n/translation-key';
@Pipe({
name: 't',
standalone: true,
pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
transform(key: TranslationKey, options?: Record<string, unknown>): string {
return t(key, options);
}
}
이제 TypeScript 코드와 템플릿 모두에서 잘못된 키에 대한 IntelliSense 자동 완성과 컴파일 타임 오류를 얻을 수 있습니다.
t('common:greeting'); // ✅ Autocomplete
t('auth:login.title'); // ✅ Valid
t('invalid.key'); // ❌ IDE shows error
{{ "common:greeting" | t }} // ✅ Valid in templates
{{ "invalid.key" | t }} // ❌ IDE shows error타입 안전 번역 키의 이점:
지능형 언어 감지를 구현하고 사용자가 런타임에 언어를 전환할 수 있도록 하여 자동 리소스 로딩을 지원하세요.
앱 시작 시 사용자의 언어를 결정하기 위해 여러 소스를 우선 순위에 따라 확인하세요.
private getInitialLanguage(): string {
if (isPlatformBrowser(this.platformId)) {
// Try to get language from localStorage (user preference)
const savedLang = localStorage.getItem('language');
if (savedLang) {
return savedLang;
}
// Fall back to browser language
return navigator.language ?? DEFAULT_LANGUAGE;
}
// Default language for SSR
return DEFAULT_LANGUAGE;
}이 접근 방식은 먼저 localStorage(사용자 선호도)를 확인하고, 그 다음 브라우저의 언어(navigator.language)로 대체하며, 마지막으로 앱의 기본 언어로 기본 설정합니다.
런타임에 언어를 전환하면서 자동으로 번역 리소스를 필요에 따라 로드하세요.
public async switchLanguage(
language: string
): Promise<void> {
// In browser, check if we need to load the translation first
if (isPlatformBrowser(this.platformId)) {
const i18next = await import('i18next');
// Check if any namespace is missing for this language
for (const namespace of NAMESPACES) {
const currentResources = i18next.default.getResourceBundle(lang, namespace);
if (!currentResources) {
// Translation not loaded yet, load it dynamically
const translationModule = await this.loadJson(lang, namespace);
i18next.default.addResourceBundle(
lang,
namespace,
translationModule.default
);
}
}
}
// Switch to the new language
await changeLanguage(lang);
// Save preference to localStorage
if (isPlatformBrowser(this.platformId)) {
localStorage.setItem('language', lang);
}
}동적 가져오기를 사용할 때 번역은 필요에 따라 로드됩니다. switchLanguage() 메서드는 getResourceBundle()을 사용하여 리소스가 이미 로드되었는지 확인합니다. 누락된 경우 동적으로 가져와 addResourceBundle()을 사용하여 추가합니다. 이는 모든 필요한 번역이 사용 가능하도록 하면서 불필요한 네트워크 요청을 방지합니다.
주요 구현 세부 사항:
여러 언어에 걸쳐 번역을 수동으로 관리하는 것은 시간 소모적이고 오류가 발생하기 쉽습니다. 여기서 AI 기반 로컬라이제이션이 필요합니다.
l10n.dev는 i18next JSON 파일을 위해 특별히 설계된 AI 기반 번역 서비스입니다:
수동 번역 작업에서 수시간을 절약하고 깨진 플레이스홀더나 잘못된 복수형과 같은 일반적인 오류를 피하세요.
Angular i18n 워크플로를 간소화할 준비가 되셨나요?
적절한 초기화 및 리소스 로딩 전략으로 Angular에서 i18next를 구현하면 원활한 국제화 경험을 보장합니다.
l10n.dev와 같은 AI 기반 번역 서비스와 결합하면 진정한 글로벌 애플리케이션을 더 빠르고 오류 없이 구축할 수 있습니다.
오늘부터 다국어 Angular 앱을 구축하기 시작하세요!