ngx-translateを使うときのTips

Angularでi18nするときはngx-translateがよさそうという話です。

## Angularのi18nについて

Angularでは公式の [i18n](https://angular.io/guide/i18n) 機能が提供されており、 Angular CLIでも辞書ファイルの生成コマンドを用意していたりと公式でのサポートも手厚い印象です。
(`angular i18n` でGoogle検索してもTopには公式のドキュメントがヒットします)
そのため、まずはこれを使ってみることも多いかと思います。
自分も、以前公式のi18n機能を使ってアプリケーションの多言語化を行ったことがありますが、その時以下の点に課題を感じていました。

- 翻訳箇所が増えた際に辞書ファイルを更新する手間がかかる
    - Angularのi18nを使用する場合、Angular CLIが提供する `ng i18n`コマンドを使って辞書ファイルの作成が行なえますが更新操作がなく都度作り直す必要があります
- htmlタグに直接記述、テンプレートシンタックスを使って表示した変数以外への対応
    - 具体的には以下のようなユースケース
        - component classから chartライブラリにラベル文字列を入力する
        - service classAngular MaterialのSnackbarのようなAPIを呼び出す

一度使ってみると、[Angularで国際化対応 - 闘うITエンジニアの覚え書き](https://www.magata.net/memo/index.php?Angular%A4%C7%B9%F1%BA%DD%B2%BD%C2%D0%B1%FE) で触れられている `2つの方法を比較した所感` の内容に完全に同意できます...

## ngx-translate

Angular公式のi18nの次に候補になるのが、 [ngx-translate](https://github.com/ngx-translate/core) ではないかと思います。
導入/基本的な使い方などは、ドキュメントも充実しているのですんなりセットアップもできました。

個人的に、 「公式でフルスタックな機能を提供していて、Angularチームが作っているものを選んでおけば間違いない」 という印象がありましたが、i18ｎ周りに関してはngx-translateを選択したほうが幸せになれそうな印象があります。
今回は、このライブラリを使った際にググりがちなポイントを何点か紹介していきたいと思います。


## いろいろなパターンへの対応

前提として、Angularプロジェクトへの導入が済んでいる状態で、`en.json` という次のような辞書ファイルが定義されていることとします。

```json:en.json
{
    "ログイン": "Member Login / Sign In",
    "認証情報を入力してください": "Input Your Accout Information",
    "ログインID": "User ID",
    "パスワード": "Password",
    ...
}
```

### 最も基本的なパターン

htmlタグに直接記述された文字列を翻訳するパターンでは、次のようにタグに `translate` ディレクティブをつけるだけです。

```html
<h2 class="title" translate>ログイン</h2>
<p class="caption" translate>認証情報を入力してください</p>
```

### button

htmlタグの`button`に対応する場合、次のように単純に `translate` ディレクティブをつけるだけでは翻訳されません。

```html:これではだめ
<button class="button" (click)="login()" translate>ログイン</button>
```

次のようにspanなどのインライン要素で翻訳箇所をラップすることで、buttonなどにも対応することができます。

```html:インライン要素で翻訳箇所をラップ
<button class="button" (click)="login()"><span translate>ログイン</span></button>
```

### placeholderなどのhtml属性

以下は、placeholderの例です。
Interpolationを使って、`translate` をpipeとして使うことでplaceholderのhtml属性を翻訳することができます。

```html
<input type="text" matInput placeholder="{{ 'ログインID' | translate }}">
```

### servieやcomponentのmodelの文言

最初の項目に挙げた次のようなユースケースです。


> - 具体的には以下のようなユースケース
>  - component classから chartライブラリにラベル文字列を入力する
>  - service classAngular MaterialのSnackbarのようなAPIを呼び出す

ngx-translateが提供する `TranslateService`の `instant()` メソッドを使うことで、serviceやcomponentのmodel側からも翻訳後の文字列を簡単に取得することができます。

```ts:AngularMaterialのSnackbarをハンドリングするクラスの例
import { Injectable } from '@angular/core';
import { MatSnackBar, MatSnackBarConfig } from '@angular/material';
import { TranslateService } from '@ngx-translate/core';

@Injectable({
  providedIn: 'root',
})
export class NotificationService {
  constructor(private readonly snackBar: MatSnackBar, private readonly translateService: TranslateService) {}

  add(message: string): void {
    this.openSnackBar(message);
  }

  addWithDismiss(message: string): void {
    this.openSnackBar(message, this.translateService.instant('閉じる'));
  }

  private openSnackBar(message: string, action?: string): void {
    const option: MatSnackBarConfig = {
      horizontalPosition: 'right',
      verticalPosition: 'bottom',
    };

    if (!action) {
      option.duration = 2000;
    }

    const translatedMessage = this.translateService.instant(message);
    this.snackBar.open(translatedMessage || message, action, option);
  }
}
```


## 参考

https://github.com/ngx-translate/core
https://angular.io/guide/i18n
https://www.magata.net/memo/index.php?Angular%A4%C7%B9%F1%BA%DD%B2%BD%C2%D0%B1%FE