国際化(i18n)は、グローバルなAngularアプリケーションを構築するために不可欠です。このガイドでは、適切なリソース読み込み、初期化、使用パターンを用いてAngularでi18nextを実装する方法を紹介します。
i18nextは最も人気のあるi18nフレームワークの1つであり、強力な機能と柔軟性を提供します:
npmまたはyarnを使用してi18nextをインストールします:
npm install i18next以上です!i18nextはフレームワーク固有の依存関係を持たないスタンドアロンライブラリです。angular-i18nextやその他のラッパーは不要で、Angularアプリケーションで直接i18nextを使用するだけです。
Angularでi18nextを使用して翻訳リソースを読み込むには、主に2つのアプローチがあります:
動的インポートを使用すると、Webpackのコード分割を活用して、オンデマンドで翻訳を読み込むことができます。このアプローチは、多くの言語や大きな翻訳ファイルを持つアプリケーションに最適です。
利点:
例:動的インポートによる読み込み
private async loadLanguageResources(
lang: string,
): Promise<Record<string, Record<string, unknown>>> {
const languageResources: Record<string, Record<string, unknown>> = {};
// Load all namespaces for the given language in parallel
await Promise.all(
NAMESPACES.map(async (namespace) => {
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が解決されるまで初期化は完了しません。これにより、翻訳されたテキストの代わりに翻訳キーが表示されるのを防ぎます。
例:App Initializerの設定
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}}." -->
補間により、翻訳に動的な値を挿入できます。変数を渡したり、フォーマットに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()を直接呼び出します。これは、通知の表示、動的なタイトルの設定、またはコンポーネントロジック内での翻訳の処理に便利です。
'loginTitle'や生のテキストではなく、'auth:login.title'のように、アプリの構造を反映したドット表記を使用してください。
メンテナンス性を高めるために、翻訳を論理的な名前空間(例: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>}`;パイプの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');
await Promise.all(
NAMESPACES.map(async (namespace) => {
const currentResources = i18next.default.getResourceBundle(
lang,
namespace,
);
// Check if any namespace is missing for this language
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アプリの構築を始めましょう!