Subscribed unsubscribe Subscribe Subscribe

angular-translate読み直し

半年前にまとめたもの。こっちに転載。


これは何か

設定例

/*@ngInject*/
export function translateConfig($translateProvider) {
  $translateProvider

  // angular-translate-loader-static-files
  // assets/lang/[langKey].json から非同期で言語ファイルを読み込む
  .useStaticFilesLoader({
    prefix: 'assets/lang/',
    suffix: '.json'
  })

  // 言語keyの設定
  .registerAvailableLanguageKeys(['en', 'ja'], {
    // en_US などは en と認識させる
    'en_*': 'en',
    'ja_*': 'ja',
    // そのほかの言語keyは全て en にする
    '*': 'en'
  })

  // ブラウザ環境から優先言語を自動取得
  // (以前に設定されたkeyがあれば、そちらを優先する <- angular-translate-storage-local利用時)
  .determinePreferredLanguage()

  // enable escaping of HTML
  // https://angular-translate.github.io/docs/#/guide/19_security
  .useSanitizeValueStrategy('escape')

  // angular-translate-handler-log
  .useMissingTranslationHandlerLog()

  // angular-translate-storage-local
  .useLocalStorage();
}

以下3つのextensionを利用している

  • angular-translate-loader-static-files
    • 非同期読み込み
  • angular-translate-handler-log
    • 対応するメッセージがない場合にエラーを表示
  • angular-translate-storage-local
    • 前回選択した言語コードを保存
    • (fallback で cookie を利用しているので angular-translate-storage-cookie も読み込まれる)

逆引き

言語ファイル(JSON)の書き方

  • 通常:
{
  "TRANSLATION_ID": "This is a concrete translation for a specific language."
}
  • ショートカット:
// bar.foo でアクセス可
{
  "bar": {
    "foo": {
      "foo": "This is my text."
    }
  }
}
  • リンク(いい機能だなあ):
{
  "SOME_NAMESPACE": {
    "OK_TEXT": "OK"
  },
  "ANOTHER_NAMESPACE": {
    "OK_TEXT": "@:SOME_NAMESPACE.OK_TEXT"
  }
}

参考: https://angular-translate.github.io/docs/#/guide/02_getting-started

$translate利用時に、非同期読み込みされたメッセージを反映

  • Two-way binding されないので 明示的に処理を書く必要がある
    • $translateChangeSuccessを利用
/*@ngInject*/
app.controller('Ctrl', ($scope, $translate, $rootScope) => {
  $rootScope.$on('$translateChangeSuccess', () => {
    $translate('HEADLINE').then(
      // 成功
      translation => $scope.headline = translation,
      // 失敗
      translationId => $scope.headline = translationId
    );
  });
});

参考: https://angular-translate.github.io/docs/#/guide/03_using-translate-service

変数を利用

言語JSONファイル:

{
  "TRANSLATION_ID": "{{username}} is logged in."
}

$translate利用時

$translate('TRANSLATION_ID', { username: 'PascalPrecht' });

$filter利用時

パターン1:

<div>{{ 'TRANSLATION_ID' | translate:'{ username: "PascalPrecht" }' }}</div>

パターン2:

<div>{{ 'TRANSLATION_ID' | translate:translationData }}</div>
angular.module('myApp')
/*@ngInject*/
.controller('Ctrl', ($scope) => { 
  $scope.translationData = {
    username: 'PascalPrecht'
  };
});

そのほかの方法もある

参考: https://angular-translate.github.io/docs/#/guide/06_variable-replacement

設定されている言語を取得

$rootScope.$on('$translateChangeEnd', (event, args) => {
  // args.language
});
// or
$translate.proposedLanguage() ||  $translate.use()
// proposedLanguage() -> 非同期読み込み中の言語keyを取得

利用言語を設定する

  • preferredLanguageを利用
  • ブラウザ環境から優先言語を自動取得する場合はdeterminePreferredLanguageを利用
    • (内部的にnavigator.languages[0] navigator.languageなどを利用している)

参考: https://angular-translate.github.io/docs/#/guide/07_multi-language

en_US en_SGなどをenとして扱う

  • registerAvailableLanguageKeysを利用

参考: https://angular-translate.github.io/docs/#/guide/09_language-negotiation

Unit Testing

非同期読み込みをしている場合、ローダー書き換える

beforeEach(module('app', ($provide, $translateProvider) => {
  $provide.factory('customLoader', $q => 
    () =>  $q.resolve()
  );
  $translateProvider.useLoader('customLoader');
}));

アプリを起動させる時の流れ

  • we register a asynchronous loader
  • we define our preferred language
  • $translate service is instantiated the first time it gets injected
  • angular-translate notices that there’s no language locally available
  • it looks if there’s a registered asynchronous loader
  • the asynchronous loader is called with the preferred language locale
  • the translation data is loaded and ready to be used

参考: https://angular-translate.github.io/docs/#/guide/22_unit-testing-with-angular-translate