Internationalization with @angular/localize

国際化の面で大きな進展がありました!@angular/localize という新しいパッケージが Angular 9.0 で導入されました。

しかし、コード内の翻訳や、コンパイル時の翻訳だけでなく実行時の翻訳など、すでに利用可能なドキュメント化されていない機能により、将来的にさらに期待することができます。

V9 の @angular/localize と CLI の助けによって何ができるかを見ることから始めましょう。

Angular の既存の国際化サポートは $localize を使用しており、次のようなテンプレートは

<h1 i18n>Hello</h1>

$localize 呼び出しにコンパイルされます。

Error: It looks like your application or one of its dependencies is using i18n.Angular 9 introduced a global `$localize()` function that needs to be loaded.Please run `ng add @angular/localize` from the Angular CLI.(For non-CLI projects, add `import '@angular/localize/init';` to your `polyfills.ts` file.For server-side rendering applications add the import to your `main.server.ts` file.)

このエラーは自明で、生成されたコードでは i18n 属性が $localize コールに変換されているため、$localize 関数をロードする必要があります。これはデフォルトで実行できますが、国際化を使用している場合にのみ必要なので、実際には意味がありません。

そのため、アプリケーションまたはその依存関係の 1 つがテンプレートで i18n 属性を使用している場合は、ポリフィルに import '@angular/localize/init' を追加する必要があります。単に次のように実行するだけです:

ng add @angular/localize

そして、CLI は依存関係にパッケージを追加し、ポリフィルに必要な import を追加します。まず、ng xi18nを実行して、メッセージをmessages.xlfファイルに展開します。次に、そのファイルをロケール、たとえばmessages.fr.xlfmessages.es.xlf用に翻訳します。

{ "projects": { "ponyracer": { "projectType": "application", // ... "i18n": { "locales": { "fr": "src/locale/messages.fr.xlf", "es": "src/locale/messages.es.xlf", } }, "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", // ... "configurations": { "production": { // ... }, "fr": { "localize": }, "es": { "localize": } } }, "serve": { // ... "configurations": { "production": { // ... }, "fr": { "browserTarget": "ponyracer:build:fr" }, "es": { "browserTarget": "ponyracer:build:es" } } } // ...}

では、es または fr 設定により、以下を実行することができます:

ng serve --configuration=fr

そして、提供するアプリは現在フランス語になっています

ng build --configuration=production,es

特定のロケールでアプリを構築することも、すべてのロケールを一度に構築することも可能です。そして、これが完了すると、ツールはコンパイルされたアプリケーションを取得し、すべての $localize 呼び出しを適切な翻訳に置き換えます。

新しいアプローチでは、コンパイルは一度行われ、その後さまざまな国際化バージョンが数秒で生成されます (可能であれば並行して生成もされます)。 🌈

そして、ロケールごとに複数のバンドルがあり、以前のようにユーザーの好みに応じて適切なものを提供できます。

この戦略はコンパイル時インライン化と呼ばれ、翻訳を直接インライン化して、実行時には何もする必要がありません。

では、まだ文書化されていない、将来的に変更されるかもしれない、しかし知っておくと面白いことについてお話ししましょう:TypeScript コードでメッセージも翻訳できるようになりました!

これまでお話ししてきた $localize 関数は直接使用することが可能です。

しかし、タグ付けされたテンプレート文字列が何であるかを説明するところから始めるべきかもしれませんね。

テンプレート文字列とタグ関数

テンプレート文字列を使用するとき、タグ関数を定義し、テンプレート文字列に適用することができます。

  • 文字列の静的部分の配列
  • 式の評価の結果

たとえば、式を含むテンプレート文字列

const person1 = 'Cedric';const person2 = 'Agnes';const template = `Hello ${person1}! Where is ${person2}?`;

があると、タグ関数が静的部分と動的部分を受け取ることになる。ここでは、主人公の名前を大文字にするタグ関数があります:

const uppercaseNames = (strings, ...values) => { // `strings` is an array with the static parts // `values` is an array with the evaluated expressions const names = values.map(name => name.toUpperCase()); // `names` now has // let's merge the `strings` and `names` arrays return strings.map((string, i) => `${string}${names ? names : ''}`).join('');};const result = uppercaseNames`Hello ${person1}! Where is ${person2}?`;// returns 'Hello CEDRIC! Where is AGNES?'

i18n with $localize in TypeScript code

$localize はこの仕組みを使用して、次のように記述します:

@Component({ template: '{{ title }}'})export class HomeComponent { title = $localize`You have 10 users`;}

なお、この関数はインポートしなくてもよいのですが、この関数を使用して、主人公を大文字にすると、主人公の名前を大文字にするタグ関数を作成します:

6867

その後、テンプレートと同じ方法でメッセージを翻訳することができます。

アプリケーションを提供し、翻訳が見つからない場合、$localize は単に元の文字列を表示し、コンソールに警告をログに記録します。

No translation found for "6480943972743237078" ("You have 10 users").

そのため、試したい場合は、与えられた ID で messages.fr.xlf に手動で追加しなければなりません:

<trans-unit> <source>You have 10 users</source> <target>Vous avez 10 utilisateurs</target></trans-unit>

私の HomeComponent のテンプレートには、Vous avez 10 utilisateurs と表示されていました!

テンプレート文字列の中に何らかの動的な式がある場合はどうなるでしょうか。

title = $localize`Hi ${this.name}! You have ${this.users.length} users.`;

式は自動的に PH および PH_1 (PH はプレースホルダ) という名前になります。

<trans-unit> <source>Hi <x/>! You have <x/> users.</source> <target>Bonjour <x/>&nbsp;! Vous avez <x/> utilisateurs.</target></trans-unit>

しかし、最良の方法は、式に意味のあるプレースホルダー名を自分で付けることで、${expression}:placeholder: 構文を使用することによってそれを行うことができます。

title = $localize`Hi ${this.name}:name:! You have ${this.users.length}:userCount: users.`;

それから、翻訳内の好きな場所でこのプレースホルダーを使用することができます。

<h1 i18n="@@home.greetings">Hello</h1>

すると、翻訳は次のようになります:

<trans-unit> <source>Hello</source> <target>Bonjour</target></trans-unit>

これは明らかに使いやすいと思います。

コード内の翻訳ではどうでしょうか。$localize では、ID を指定する構文も理解できます。

テンプレートの構文と同様に、翻訳者が文脈を把握しやすくするために、説明と意味を指定することもできます。

ランタイム翻訳

私が言及していたように、上記の CLI コマンド (ng serve --configuration=fr または ng build --localize) を使用する場合、アプリケーションはコンパイルされてからブラウザで表示する前に翻訳されるので、ランタイムに $localize を呼び出す必要はありません。 つまり、$localize 呼び出しを含む 1 つのアプリケーションだけを出荷して、アプリケーションが起動する前に、必要な翻訳をロードすることができます。

import '@angular/localize/init';import { loadTranslations } from '@angular/localize';loadTranslations({ '1815172606781074132': 'Bonjour {$name}\xa0! Vous avez {$userCount} utilisateurs.'});

見てわかるように、ロケールを考慮する必要はありません。

これで、単純な ng serve を実行すると、タイトルがフランス語で表示されます!また、ng xi18nmessages.fr.xlf は必要なく、angular.json の各ロケールのための特定の設定も不要です。長期的には、これが適切にサポートおよび文書化されると、ほとんどの国際化ライブラリのように、実行時に JSON ファイルをロードできるようになるはずです。v9 でそれを達成することもできます。それは少し手動での作業ですが、可能です。

What about changing the locale on the fly? しかし、ブラウザーの更新を気にしないのであれば、可能です。

  • ユーザーが新しい言語 (たとえば、スペイン語) を選択します。
  • ブラウザで言語を保存します (たとえば localStorage に)。
  • ページを再ロードし、アプリケーションを再起動します polyfills.ts で、保存されている言語を読み込んで開始します
  • loadTranslations でスペイン語の翻訳を正しく読み込むように設定します。

もちろん、これは将来的に Angular の将来のバージョンで、またはエコシステムからのライブラリによって、よりスムーズになるでしょう。とにかく、私たちはアプリケーションのバージョンを 1 つだけ出荷して、実行時に翻訳をロードするだけに近づきつつあります。

Leave a Reply