web-dev-qa-db-ja.com

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

私はAngularJSアプリケーションの国際化に 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番目のコントローラには生の文字列を渡したいだけです)。

質問

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

118
ndequeker

EDIT:より良い解決策についてはPascalPrecht(angle-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がすることなので、それはそれほど悪いことではありません...

68

推奨:コントローラに翻訳せず、ビューに翻訳

私はあなたのコントローラを翻訳ロジックから解放し、このようにあなたのビューの中であなたの文字列を直接翻訳することをお勧めします:

<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;
        });
});

翻訳サービスには、$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を監視解除するのに十分賢いです。

139

実際には、代わりにtranslateディレクティブを使うべきです。

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

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

しかし、回避策がなく、実際にコントローラでする必要がある _ $translateサービスを使用する場合は、$translateChangeSuccess$translate.instant()と組み合わせて使用​​して、呼び出しを$rootScopeイベントにラップする必要があります。

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

では、なぜ$rootScopeではなく$scopeなのでしょうか。その理由は、angle-translateのイベントでは、スコープ階層全体をブロードキャストする必要がないため、$emitでは$rootScopeされるのではなく、$broadcast$scopeされるということです。

なぜ$translate.instant()ではなく$translate()を非同期にするのですか? $translateChangeSuccessイベントが発生すると、必要な翻訳データが存在し、非同期実行が行われていないことが確実になります(たとえば、非同期ローダー実行)。したがって、同期が可能で$translate.instant()を使用できます。

バージョン2.8.0以降、$translate.onReady()もあり、これは翻訳の準備ができ次第すぐに解決される約束を返します。 変更履歴を参照

122
Pascal Precht

コントローラで翻訳するには$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サービスをメソッドにカプセル化し、それをコントローラ内および$translateChangeSucesslistener内で呼び出すこともできます。

5
MacLeod

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

この問題を正常にデバッグできたとしても、より大きな問題は、関連する開発作業が非常に大きいということです。開発者はサイト上のすべての文字列を手動で抽出し、それを.jsonファイルに入れて、手動で文字列コードで参照する必要があります(この場合は 'pageTitle')。ほとんどの商用サイトには、これが起こる必要がある何千もの文字列があります。そしてそれは始まりに過ぎません。基礎となるテキストが変更されたときに翻訳を同期させるシステム、翻訳ファイルをさまざまな翻訳者に送信するシステム、翻訳ファイルをビルドに再統合するシステム、サイトを再デプロイして翻訳者が確認できるシステムが必要です。彼らの文脈の変化、そして何度も。

また、これは「バインディング」のイベントベースのシステムであるため、ページ上のすべての文字列に対してイベントが発生します。これは、ページを変換するのに時間がかかるだけでなく、ページ上のすべてのアクションを遅くすることもできます。大量のイベントを追加し始めた場合.

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

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

1
Jeff W