コントローラーでの角度変換の正しい使い方


121

AngularJSアプリケーションでi18nのangular-translateを使用しています。

すべてのアプリケーションビューに対して、専用のコントローラーがあります。以下のコントローラーでは、ページタイトルとして表示される値を設定します。

コード

HTML

<h1>{{ pageTitle }}</h1>

JavaScript

.controller('FirstPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
        $scope.pageTitle = $filter('translate')('HELLO_WORLD');
    }])

.controller('SecondPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
        $scope.pageTitle = 'Second page title';
    }])

angular-translate-loader-url拡張機能を使用して翻訳ファイルをロードしています。

問題

最初のページの読み込み時に、そのキーの翻訳の代わりに翻訳キーが表示されます。翻訳はHello, World!ですが見HELLO_WORLDます。

2回目にこのページにアクセスすると、問題はなく、翻訳されたバージョンが表示されます。

この問題は、コントローラーがに値を割り当てているときに変換ファイルがまだ読み込まれていない可能性があるという事実に関係していると思います$scope.pageTitle

リマーク

<h1>{{ pageTitle | translate }}</h1>およびを使用すると$scope.pageTitle = 'HELLO_WORLD';、翻訳は最初から完璧に機能します。この問題は、常に翻訳を使用する必要がないことです(たとえば、2番目のコントローラーの場合、生の文字列を渡したいだけです)。

質問

これは既知の問題/制限ですか?これをどのように解決できますか?

回答:


69

編集:より良い解決策については、PascalPrecht(angular-translateの作者)からの回答を参照してください。


ロードの非同期の性質が問題の原因です。ご覧の{{ pageTitle | translate }}とおり、Angularは式を監視します。ローカリゼーションデータが読み込まれると、式の値が変更され、画面が更新されます。

だから、あなたは自分でそれを行うことができます:

.controller('FirstPageCtrl', ['$scope', '$filter', function ($scope, $filter) {
    $scope.$watch(
        function() { return $filter('translate')('HELLO_WORLD'); },
        function(newval) { $scope.pageTitle = newval; }
    );
});

ただし、これにより、すべてのダイジェストサイクルで監視対象の式が実行されます。これは最適ではなく、目に見えるパフォーマンスの低下を引き起こす場合とそうでない場合があります。とにかくそれはAngularがすることなので、それはそれほど悪いことではありません...


ありがとうございました!ビューまたはコントローラーでフィルターを使用しても、まったく同じように動作するはずです。ここではそうではないようです。
ndequeker 2013

$scope.$watchAngular Translateはコントローラーで使用されるサービスを提供しているので、aの使用はかなりやりすぎだと思います。以下の私の答えを参照してください。
ロビンファンバーレン2014年

1
Angular Translateフィルターは$translate.instant()サービスと同じなので、必要ありません。これとは別に、パスカルの答えに注意してください。
knalli 2014年

$ watchの使用はやり過ぎです。以下の回答はより適切な使用法です。
jpblancoder、2014年

141

推奨:コントローラーでは翻訳せず、ビューで翻訳してください

コントローラを翻訳ロジックから解放し、次のようにビュー内で文字列を直接翻訳することをお勧めします。

<h1>{{ 'TITLE.HELLO_WORLD' | translate }}</h1>

提供されるサービスを利用する

Angular Translateは、$translateコントローラーで使用できるサービスを提供します。

$translateサービスの使用例は次のとおりです。

.controller('TranslateMe', ['$scope', '$translate', function ($scope, $translate) {
    $translate('PAGE.TITLE')
        .then(function (translatedValue) {
            $scope.pageTitle = translatedValue;
        });
});

変換サービスには、promiseを処理する必要なく文字列を直接変換するためのメソッドもあります$translate.instant()

.controller('TranslateMe', ['$scope', '$translate', function ($scope, $translate) {
    $scope.pageTitle = $translate.instant('TITLE.DASHBOARD'); // Assuming TITLE.DASHBOARD is defined
});

を使用$translate.instant()する場合の欠点は、言語ファイルを非同期でロードしている場合、言語ファイルがまだロードされていないことです。

提供されたフィルターの使用

私はこの方法でプロミスを処理する必要がないので、これが私の好みの方法です。フィルターの出力は、スコープ変数に直接設定できます。

.controller('TranslateMe', ['$scope', '$filter', function ($scope, $filter) {
    var $translate = $filter('translate');

    $scope.pageTitle = $translate('TITLE.DASHBOARD'); // Assuming TITLE.DASHBOARD is defined
});

提供されたディレクティブの使用

@PascalPrechtはこの素晴らしいライブラリの作成者なので、彼のアドバイスに従って(以下の彼の回答を参照)、翻訳を非常にインテリジェントに処理するように見える提供されたディレクティブを使用することをお勧めします。

ディレクティブは非同期実行を処理し、変換に動的な値がない場合はスコープの変換IDを監視しないように十分に賢いです。


その無関係なコメントを書く代わりにあなたがそれを試したなら、あなたは今までに答えを知っているでしょう。短い答え:はい。それは可能です。
Robin van Baalen、2015年

1
コントローラーのフィルターを使用した例:instant()と同様に、言語ファイルがロードされていない場合、これは正しく機能しませんか?その場合は時計を使うべきではないでしょうか。または、「翻訳が読み込まれていることがわかっている場合にのみフィルターを使用しますか?」
ボンビノッシュ2015年

@Bombinosh翻訳が読み込まれていることがわかっている場合は、フィルターメソッドを使用します。個人的には、必要がない場合は、翻訳を動的にロードしないことをお勧めします。これはアプリケーションの必須部分であるため、ユーザーがそれを待たないようにすることをお勧めします。しかし、それは個人的な意見です。
ロビンファンバーレン2015

翻訳のポイントは、ユーザーの好みによって、またはユーザーのアクションによっても変更できることです。したがって、一般的には、動的にロードする必要があります。少なくとも、翻訳する文字列の数が重要な場合、および/または多くの翻訳がある場合。
PhiLho、2015年

4
HTMLで変換が行われると、ダイジェストサイクルは2回実行されますが、コントローラーでは1回のみ実行されます。99%の場合、これはおそらく問題ではありませんが、多くのセルで変換が行われる角度のあるuiグリッドでひどいパフォーマンスの問題がありました。確かにエッジケース、知っておくべきこと
tykowale

123

実際には、代わりにそのようなもののために翻訳ディレクティブを使用する必要があります。

<h1 translate="{{pageTitle}}"></h1>

ディレクティブは非同期実行を処理し、変換に動的な値がない場合はスコープの変換IDを監視しないように十分に賢いです。

ただし、回避策がなく$translate、コントローラーでサービスを実際使用する必要がある場合は、次のように$translateChangeSuccess使用$rootScopeして、呼び出しをイベントにラップする必要があります$translate.instant()

.controller('foo', function ($rootScope, $scope, $translate) {
  $rootScope.$on('$translateChangeSuccess', function () {
    $scope.pageTitle = $translate.instant('PAGE.TITLE');
  });
})

なぜ$rootScopeありませんか$scope?その理由は、angular-translateのイベントでは、スコープ階層全体をブロードキャストする必要がないため、イベントが$emitオンになるの$rootScopeではなく$broadcastオンになる$scopeためです。

なぜ$translate.instant()非同期ではないの$translate()ですか?ときに$translateChangeSuccessイベントが発生し、それゆえ我々だけで使用することができ、必要な変換データがあると何の非同期実行は、(例えば、非同期ローダーの実行のために)起こっていないことを確信して$translate.instant()同期していると、ちょうど翻訳が利用可能であることを前提としています。

バージョン2.8.0以降にもがあり$translate.onReady()、翻訳の準備ができるとすぐに解決されるpromiseを返します。changelogを参照してください


フィルターの代わりに変換ディレクティブを使用すると、パフォーマンスの問題が発生する可能性がありますか?また、内部的には、instant()の戻り値を監視しています。それで、現在のスコープが破棄されたときに時計を削除しますか?
Nilesh 2014年

私はあなたの提案を使ってみましたが、スコープ変数の値が動的に変化すると機能しません。
Nilesh 2014年

10
実際には、常に新しい時計を設定するためにアプリの処理速度が低下するため、可能な限りフィルターを使用しない方が常に適切です。ただし、指令は少し進んでいます。翻訳IDの値を監視する必要があるかどうかをチェックします。これにより、アプリのパフォーマンスが向上します。プランクを作ってリンクしてくれませんか。詳しく見ていきましょう。
Pascal Precht 2014年

プランク:plnkr.co/edit/j53xL1EdJ6bT20ldlhxrおそらく私の例では、ディレクティブは値を監視しないことを決定しています。また、別の問題として、キーが見つからない場合にカスタムエラーハンドラーが呼び出されますが、返された文字列は表示されません。私はそれのために別のプランクを作ります。
Nilesh 2014年

2
@PascalPrecht質問ですが、翻訳でbind-onceを使用することは良い習慣ですか?このように{{::'HELLO_WORLD | translate}}'
Zunair Zubair 2015

5

コントローラで翻訳を行うには、$translateサービスを使用できます:

$translate(['COMMON.SI', 'COMMON.NO']).then(function (translations) {
    vm.si = translations['COMMON.SI'];
    vm.no = translations['COMMON.NO'];
});

このステートメントは、コントローラーのアクティブ化時にのみ変換を行いますが、実行時の言語の変更を検出しません。その動作を実現するには、$rootScopeイベントをリッスンし、$translateChangeSuccessそこで同じ翻訳を行うことができます。

    $rootScope.$on('$translateChangeSuccess', function () {
        $translate(['COMMON.SI', 'COMMON.NO']).then(function (translations) {
            vm.si = translations['COMMON.SI'];
            vm.no = translations['COMMON.NO'];
        });
    });

もちろん、$translateサービスをメソッドにカプセル化して、コントローラーと$translateChangeSucessリスナーで呼び出すこともできます。


1

起こっているのは、Angular-translateがイベントベースのシステムで式を監視していることであり、他のバインディングまたは双方向バインディングの場合と同様に、データが取得され、値が変更されると、イベントが発生します。明らかに翻訳には使えません。もちろん、ページ上の他の動的データとは異なり、翻訳データはユーザーにすぐに表示される必要があります。ページが読み込まれた後はポップインできません。

この問題を正常にデバッグできたとしても、より大きな問題は、関連する開発作業が巨大であることです。開発者は、サイト上のすべての文字列を手動で抽出し、.jsonファイルに入れ、文字列コード(つまり、この場合は 'pageTitle')で手動で参照する必要があります。ほとんどの商用サイトには何千もの文字列があり、これを実現する必要があります。そしてそれはほんの始まりにすぎません。いくつかの基になるテキストが変更されたときに翻訳の同期を維持するシステム、翻訳ファイルをさまざまなトランスレーターに送信するシステム、ビルドに再統合するシステム、サイトを再デプロイしてトランスレーターが見えるようにするシステムが必要です。状況の変化、そして継続的な変化。

また、これは「バインディング」のイベントベースのシステムであるため、ページ上のすべての文字列に対してイベントが発生します。これは、ページを変換するための遅い方法であるだけでなく、ページ上のすべてのアクションを遅くする可能性があります。それに多数のイベントを追加し始めた場合。

とにかく、後処理翻訳プラットフォームを使用することは、私にとってより理にかなっています。たとえば、GlobalizeItを使用すると、翻訳者はサイトのページに移動して、そのページの言語に応じたテキストの編集を開始できます。それだけです:https : //www.globalizeit.com/HowItWorks。プログラミングは必要ありません(プログラムで拡張可能です)。Angularと簡単に統合できます:https : //www.globalizeit.com/Translate/Angular、ページの変換は一度に行われ、常に翻訳されたテキストが表示されますページの最初のレンダリング。

完全な開示:私は共同創設者です:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.