国際化(i18n)は、グローバルなAngularアプリケーションを構築するために不可欠です。このガイドでは、適切なリソースの読み込み、初期化、使用パターンを用いて、Angularでi18nextを実装する方法を示します。
i18nextは最も人気のあるi18nフレームワークの1つであり、強力な機能と柔軟性を提供します:
npmまたはyarnを使用してi18nextをインストールします:
npm install i18nextこれで完了です!i18nextはスタンドアロンライブラリで、フレームワーク固有の依存関係はありません。angular-i18nextや他のラッパーは必要なく、Angularアプリケーションで直接i18nextを使用できます。
i18nextを使ったAngularでの翻訳リソースの読み込みには主に2つのアプローチがあります:
動的インポートを使用すると、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);
}
}このパイプは、言語が変更されたときに更新されるように純粋ではない(pure: 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の組み込みの複数形サポートを使用して、すべての言語で単数/複数形を正しく処理します。
アプリをレンダリングする前に、i18nextが初期化されていることを常に確認し、アプリのブートストラップ中にprovideAppInitializer()を使用して初期化を実行します。
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>}`;パイプのtransformメソッドをstringの代わりに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アプリを構築し始めましょう!