アプリを国際化することは、ソフトウェア開発を苦痛な経験にする可能性があります。特に、最初から始めない場合や、アプリに対して意欲的なアプローチをとる場合はなおさらです。
ダミーのモンテカルロシミュレーション
フロントエンドとバックエンドが明確に分離されている最新のアプリは、国際化に関してはさらに扱いが難しい場合があります。突然、従来のサーバーサイドページで生成されたWebアプリの国際化に役立った、実績のある多数のツールにアクセスできなくなります。
したがって、AngularJSアプリでは、適切なロケールでレンダリングするために、国際化(i18n)およびローカリゼーション(l10n)データをクライアントにオンデマンドで配信する必要があります。従来のサーバー側でレンダリングされたアプリとは異なり、すでにローカライズされているページを配信するためにサーバーに依存することはできなくなりました。多言語PHPアプリケーションの構築について学ぶことができます ここに
この記事では、国際化する方法を学びます AngularJS アプリ、およびプロセスを容易にするために使用できるツールについて学習します。 AngularJSアプリを多言語化すると、いくつかの興味深い課題が発生する可能性がありますが、特定のアプローチにより、これらの課題のほとんどを簡単に回避できるようになります。
クライアントがユーザーの好みに基づいてその場で言語とロケールを変更できるようにするには、いくつかの重要な設計上の決定を行う必要があります。
これらの質問にできるだけ早く答えることで、開発プロセスの障害を回避できます。これらの課題のそれぞれは、この記事で扱われます。堅牢なAngularJSライブラリを介したものもあれば、特定の戦略やアプローチを介したものもあります。
AngularJSアプリを国際化するために特別に構築されたJavaScriptライブラリがいくつかあります。
angular-translate
は、i18nデータを非同期でロードする機能とともに、フィルターとディレクティブを提供するAngularJSモジュールです。複数化をサポートします MessageFormat
、および高度に拡張可能で構成可能になるように設計されています。
angular-translate
を使用している場合プロジェクトでは、次のパッケージのいくつかが非常に役立つ場合があります。
angular-sanitize
:を防ぐために使用できます XSS攻撃 翻訳で。angular-translate-interpolation-messageformat
:性別に依存するテキストフォーマットをサポートする複数化。angular-translate-loader-partial
:翻訳された文字列をクライアントに配信するために使用されます。真にダイナミックな体験のために、あなたは追加することができます angular-dynamic-locale
束に。このライブラリを使用すると、ロケールを動的に変更できます。これには、日付、数値、通貨などがすべてフォーマットされる方法が含まれます。
AngularJSボイラープレートの準備ができていると仮定すると、NPMを使用して国際化パッケージをインストールできます。
npm i -S angular-translate angular-translate-interpolation-messageformat angular-translate-loader-partial angular-sanitize messageformat
パッケージがインストールされたら、アプリの依存関係としてモジュールを追加することを忘れないでください。
// /src/app/core/core.module.js app.module('app.core', ['pascalprecht.translate', ...]);
モジュールの名前はパッケージの名前とは異なることに注意してください。
アプリにテキスト付きのツールバーとプレースホルダーテキスト付きのフィールドがあるとします。
Hello ...
上記のビューには、国際化できる2ビットのテキスト「Hello」と「Search」があります。 HTMLに関しては、1つはアンカータグの内部テキストとして表示され、もう1つは属性の値として表示されます。
それらを国際化するには、両方の文字列リテラルをトークンに置き換える必要があります。トークンは、ページのレンダリング中に、AngularJSがユーザーの好みに基づいて実際に翻訳された文字列に置き換えることができます。
AngularJSは、トークンを使用して、指定した変換テーブルでルックアップを実行することでこれを行うことができます。モジュールangular-translate
これらの変換テーブルは、プレーンJavaScriptオブジェクトまたはJSONオブジェクト(リモートでロードする場合)として提供されることを想定しています。
これらの変換テーブルが一般的にどのように見えるかの例を次に示します。
// /src/app/toolbar/i18n/en.json { 'TOOLBAR': { 'HELLO': 'Hello', 'SEARCH': 'Search' } } // /src/app/toolbar/i18n/tr.json { 'TOOLBAR': { 'HELLO': 'Merhaba', 'SEARCH': 'Ara' } }
上からツールバービューを国際化するには、文字列リテラルを、AngularJSが変換テーブルで検索するために使用できるトークンに置き換える必要があります。
{ translate}
内部テキストの場合、translate
を使用する方法に注意してください。ディレクティブまたはtranslate
フィルタ。 (translate
ディレクティブについて詳しく知ることができます ここに と約translate
フィルタ ここに 。)
これらの変更により、ビューがレンダリングされると、angular-translate
TOOLBAR.HELLO
に対応する適切な翻訳を自動的に挿入します現在の言語に基づいてDOMに変換します。
属性値として表示される文字列リテラルをトークン化するには、次のアプローチを使用できます。
// /src/app/toolbar/i18n/en.json { 'TOOLBAR': { 'HELLO': 'Hello, {{name}}.' } }
トークン化された文字列に変数が含まれている場合はどうなりますか?
「Hello、{{name}}。」のようなケースを処理するために、AngularJSがすでにサポートしているのと同じ補間構文を使用して変数置換を実行できます。
翻訳表:
{{'TOOLBAR.HELLO | translate:'{ name: vm.user.name }'}}
その後、さまざまな方法で変数を定義できます。ここにいくつかあります:
He saw 1 person(s) on floor 1. She saw 1 person(s) on floor 3. Number of people seen on floor 2: 2.
i18nとl10nに関しては、複数化はかなり難しいトピックです。言語や文化が異なれば、言語がさまざまな状況で複数形を処理する方法について異なるルールがあります。
これらの課題のために、ソフトウェア開発者は単に問題に対処しない(または少なくとも適切に対処しない)ことがあり、その結果、ソフトウェアは次のようなばかげた文章を生成します。
He saw 1 person on the 2nd floor. She saw 1 person on the 3rd floor. They saw 2 people on the 5th floor.
幸いなことに、 標準 これを処理する方法については、標準のJavaScript実装は次のように利用できます。 MessageFormat 。
MessageFormatを使用すると、上記の構造が不十分な文を次のように置き換えることができます。
MessageFormat
var message = [ '{GENDER, select, male{He} female{She} other{They}}', 'saw', '{COUNT, plural, =0{no one} one{1 person} other{# people}}', 'on the', '{FLOOR, selectordinal, one{#st} two{#nd} few{#rd} other{#th}}', 'floor.' ].join(' ');
次のような式を受け入れます。
var messageFormatter = new MessageFormat('en').compile(message); messageFormatter({ GENDER: 'male', COUNT: 1, FLOOR: 2 }) // 'He saw 1 person on the 2nd floor.' messageFormatter({ GENDER: 'female', COUNT: 1, FLOOR: 3 }) // 'She saw 1 person on the 3rd floor.' messageFormatter({ COUNT: 2, FLOOR: 5 }) // 'They saw 2 people on the 5th floor.'
上記の配列を使用してフォーマッターを作成し、それを使用して文字列を生成できます。
MessageFormat
angular-translate
の使い方angular-translate
でアプリ内でその全機能を利用するには?
アプリの設定では、単に/src/app/core/core.config.js app.config(function ($translateProvider) { $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); });
と伝えますそのメッセージ形式の補間は、次のように利用できます。
// /src/app/main/social/i18n/en.json { 'SHARED': '{GENDER, select, male{He} female{She} other{They}} shared this.' }
変換テーブルのエントリは次のようになります。
{{ 'SHARED' | translate:'{ GENDER: 'male' }':'messageformat' }}
そして、ビューで:
$translateProvider
ここでは、AngularJSのデフォルトの補間器の代わりにメッセージ形式の補間器を使用する必要があることを明示的に指定する必要があります。これは、2つの補間器の構文がわずかに異なるためです。あなたはこれについてもっと読むことができます ここに 。
AngularJSが翻訳テーブルからトークンの翻訳を検索する方法がわかったので、アプリは最初に翻訳テーブルをどのように認識しますか?どのロケール/言語を使用する必要があるかをアプリにどのように伝えますか?
ここでcore.config.js
について学びます。
アプリの// /src/app/core/core.config.js app.config(function ($translateProvider) { $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); $translateProvider.translations('en', { TOOLBAR: { HELLO: 'Hello, {{name}}.' } }); $translateProvider.translations('tr', { TOOLBAR: { HELLO: 'Merhaba, {{name}}.' } }); $translateProvider.preferredLanguage('en'); });
で直接サポートするロケールごとに翻訳テーブルを提供できます次のようにファイルします。
// /src/app/toolbar/toolbar.controller.js app.controller('ToolbarCtrl', function ($scope, $translate) { $scope.changeLanguage = function (languageKey) { $translate.use(languageKey); // Persist selection in cookie/local-storage/database/etc... }; });
ここでは、現在の言語を英語(en)として宣言しながら、英語(en)およびトルコ語(tr)のJavaScriptオブジェクトとして翻訳テーブルを提供しています。ユーザーが言語を変更したい場合は、 $ translateサービス :
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.determinePreferredLanguage(); });
デフォルトでどの言語を使用すべきかという問題はまだあります。アプリの初期言語をハードコーディングすると、常に受け入れられるとは限りません。このような場合、別の方法は、$ translateProviderを使用して言語を自動的に決定しようとすることです。
determinePreferredLanguage
window.navigator
partialLoader
の値を検索しますそして、明確な信号がユーザーによって提供されるまで、インテリジェントなデフォルトを選択します。
前のセクションでは、JavaScriptオブジェクトとしてソースコードに直接変換テーブルを提供する方法を示しました。これは小さなアプリケーションでは受け入れられるかもしれませんが、このアプローチはスケーラブルではありません。そのため、変換テーブルはリモートサーバーからJSONファイルとしてダウンロードされることがよくあります。
この方法で変換テーブルを維持すると、クライアントに配信される初期ペイロードサイズが減少しますが、さらに複雑になります。ここで、i18nデータをクライアントに配信するという設計上の課題に直面しています。これを注意深く処理しないと、アプリケーションのパフォーマンスが不必要に低下する可能性があります。
なぜそんなに複雑なのですか? AngularJSアプリケーションはモジュールに編成されています。複雑なアプリケーションでは、多くのモジュールがあり、それぞれに独自のi18nデータがあります。したがって、i18nデータを一度にロードして提供するなどの単純なアプローチは避ける必要があります。
必要なのは、i18nデータをモジュールごとに整理する方法です。これにより、必要なときに必要なものだけをロードし、以前にロードしたものをキャッシュして、同じデータのリロードを回避できます(少なくともキャッシュが無効になるまで)。
ここが/src/app/main/i18n/en.json /src/app/main/i18n/tr.json /src/app/toolbar/i18n/en.json /src/app/toolbar/i18n/tr.json
です登場します。
コードの実行にかかった時間を示すことができるモジュールはどれですか?
アプリケーションの変換テーブルが次のように構成されているとします。
$translateProvider
partialLoader
を構成できます使用する// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.useLoader('$translatePartialLoader', { urlTemplate: '/src/app/{part}/i18n/{lang}.json' }); });
この構造に一致するURLパターンを使用します。
$translatePartialLoader
当然のことながら、「lang」は実行時に言語コードに置き換えられます(例:「en」または「tr」)。 「パーツ」はどうですか? $ translateProviderは、ロードする「パーツ」をどのように認識しますか?
この情報は、コントローラー内で// /src/app/main/main.controller.js app.controller('MainCtrl', function ($translatePartialLoader) { $translatePartialLoader.addPart('main'); }); // /src/app/toolbar/toolbar.config.js app.controller('ToolbarCtrl', function ($translatePartialLoader) { $translatePartialLoader.addPart('toolbar'); });
を使用して提供できます。
$translateProvider
これでパターンが完成し、特定のビューのi18nデータは、そのコントローラーが最初に実行されたときに読み込まれます。これはまさに必要なものです。
キャッシングはどうですか?
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.useLoaderCache(true); // default is false });
を使用して、アプリ構成で標準キャッシュを有効にできます。
$translate
特定の言語のキャッシュを無効にする必要がある場合は、$translate.refresh(languageKey); // omit languageKey to refresh all
を使用できます。
angular-dynamic-locale
これらの要素を適切に配置すると、アプリケーションは完全に国際化され、複数の言語をサポートします。
このセクションでは、npm i -S angular-dynamic-locale angular-i18n
の使用方法を学習しますAngularJSアプリケーションで、数値、通貨、日付などのUI要素のフォーマットをサポートします。
このためには、さらに2つのパッケージをインストールする必要があります。
// /src/app/core/core.module.js app.module('app.core', ['tmh.dynamicLocale', ...]);
パッケージがインストールされたら、モジュールをアプリの依存関係に追加できます。
angular-locale_en-us.js
ロケールルールは、日付、数値、通貨などを、に依存するコンポーネントによってフォーマットする方法の仕様を提供する単純なJavaScriptファイルです。 $ローカル サービス。
現在サポートされているロケールのリストが利用可能です ここに 。
これが... 'MONTH': [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], 'SHORTDAY': [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ], ...
の抜粋です月と日付のフォーマットの説明:
angular-dynamic-locale
i18nデータとは異なり、ロケールルールはアプリケーションに対してグローバルであるため、特定のロケールのルールを一度にロードする必要があります。
デフォルトでは、angular/i18n/angular-locale_{{locale}}.js
ロケールルールファイルがtmhDynamicLocaleProvider
にあることを想定しています。他の場所にある場合は、// /src/app/core/core.config.js app.config(function (tmhDynamicLocaleProvider) { tmhDynamicLocaleProvider.localeLocationPattern( '/node_modules/angular-i18n/angular-locale_{{locale}}.js'); });
デフォルトをオーバーライドするには、次を使用する必要があります。
tmhDynamicLocaleCache
キャッシングはangular-dynamic-locale
によって自動的に処理されますサービス。
ロケールルールは文字列変換よりも変更される可能性が低いため、ここではキャッシュの無効化はそれほど問題になりません。
ロケールを切り替えるには、tmhDynamicLocale
// /src/app/toolbar/toolbar.controller.js app.controller('ToolbarCtrl', function ($scope, tmhDynamicLocale) { $scope.changeLocale = function (localeKey) { tmhDynamicLocale.set(localeKey); // Persist selection in cookie/local-storage/database/etc... }; });
を提供しますサービス:
angular-i18n
ロケールルールはimport gulp from 'gulp'; import map from 'map-stream'; import rename from 'gulp-rename'; import traverse from 'traverse'; import transform from 'vinyl-transform'; import jsonFormat from 'gulp-json-format'; function translateTable(to) { return transform(() => { return map((data, done) => { const table = JSON.parse(data); const strings = []; traverse(table).forEach(function (value) { if (typeof value !== 'object') { strings.push(value); } }); Promise.all(strings.map((s) => getTranslation(s, to))) .then((translations) => { let index = 0; const translated = traverse(table).forEach(function (value) { if (typeof value !== 'object') { this.update(translations[index++]); } }); done(null, JSON.stringify(translated)); }) .catch(done); }); }); } function translate(to) { return gulp.src('src/app/**/i18n/en.json') .pipe(translateTable(to)) .pipe(jsonFormat(2)) .pipe(rename({ basename: to })) .pipe(gulp.dest('src/app')); } gulp.task('translate:tr', () => translate('tr')); This task assumes the following folder structure: /src/app/main/i18n/en.json /src/app/toolbar/i18n/en.json /src/app/navigation/i18n/en.json ...
に付属していますパッケージなので、必要に応じてパッケージの内容をアプリケーションで利用できるようにするだけです。しかし、翻訳テーブルのJSONファイルをどのように生成しますか?ダウンロードしてアプリケーションにプラグインできるパッケージは正確にはありません。
1つのオプションは、特にアプリケーションの文字列が変数や複数の式を含まない単純なリテラルである場合に、プログラムによる翻訳APIを使用することです。
と gulp そして、いくつかの追加パッケージを使用して、アプリケーションのプログラムによる翻訳を要求するのは簡単です。
/src/app/main/i18n/en.json /src/app/main/i18n/tr.json /src/app/toolbar/i18n/en.json /src/app/toolbar/i18n/tr.json /src/app/navigation/i18n/en.json /src/app/navigation/i18n/tr.json ...
スクリプトは、最初にすべての英語の翻訳テーブルを読み取り、文字列リソースの翻訳を非同期的に要求してから、英語の文字列を翻訳された文字列に置き換えて、新しい言語の翻訳テーブルを作成します。
最後に、新しい翻訳テーブルは英語の翻訳テーブルの兄弟として書き込まれ、次のようになります。
getTranslation
import bluebird from 'bluebird'; import MicrosoftTranslator from 'mstranslator'; bluebird.promisifyAll(MicrosoftTranslator.prototype); const Translator = new MicrosoftTranslator({ client_id: process.env.MICROSOFT_TRANSLATOR_CLIENT_ID, client_secret: process.env.MICROSOFT_TRANSLATOR_CLIENT_SECRET }, true); function getTranslation(string, to) { const text = string; const from = 'en'; return Translator.translateAsync({ text, from, to }); }
の実装また、簡単です。
angular-translate
ここでは、 Microsoft Translate 、しかし、次のような別のプロバイダーを簡単に使用できます。 グーグル翻訳 または ヤンデックス翻訳 。
プログラムによる翻訳は便利ですが、次のようないくつかの欠点があります。
これらの場合やその他の場合、人間による翻訳が必要になる場合があります。ただし、それは別のブログ投稿のトピックです。
この記事では、これらのパッケージを使用して国際化およびローカライズする方法を学びました AngularJSアプリケーション 。
angular-dynamic-locale
、gulp
、および
|_+_|は、AngularJSアプリケーションを国際化するための強力なツールであり、面倒な低レベルの実装の詳細をカプセル化します。
この投稿で説明されているアイデアを説明するデモアプリについては、こちらをご覧ください GitHubリポジトリ 。