【Angular】ngx-translateを使った多言語化で、複数の多言語ファイルを利用したい

## はじめに
タイトルのユースケース例としては、複数のAngularアプリが存在しそれらには固有のアプリIDが振られておりそれぞれのアプリで文言のカスタマイズができるというケース。そこで、

- どのアプリにも共通して利用される文言
- アプリごとに設定された文言

この２種類の多言語化を必要としたとき「[ngx-translate](https://github.com/ngx-translate/core)」を使っている場合は複数の多言語ファイルで管理したくなる。そのときどうしたら実現できるかを共有していく。

## まずはインストール
```bash
$ npm install --save @ngx-translate/core @ngx-translate/http-loader
```

## app.module.tsに設定を追加
※`useFactory`の部分が無いが、ここはのちに追加する。

```diff:app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
+ import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';

@NgModule({
    declarations: [AppComponent],
    imports: [
        BrowserModule,
        AppRoutingModule,
        CoreModule,
        CoreModule,
+       TranslateModule.forRoot({
+           loader: {
+               provide: TranslateLoader,
+               useFactory: ,
+               deps: [HttpClient],
+           },
+           isolate: false,
+       }),
    ],
    providers: [],
    bootstrap: [AppComponent],
})
export class AppModule {}
```

## 複数の多言語ファイルをロードするため、独自のLoaderを実装
```typescript:multi-translate-http-loader.ts
import { TranslateLoader } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http';
import { forkJoin, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

/**
 * 複数の多言語ファイルを読み込むためのカスタムTranslateHttpLoader
 */
class MultiTranslateHttpLoader implements TranslateLoader {
    constructor(
        private http: HttpClient,
        public resources: { prefix: string; suffix: string }[] = [
            {
                prefix: '/assets/language/',
                suffix: '.json',
            },
        ]
    ) {}

    public getTranslation(lang: string): Observable<object> {
        return forkJoin(
            this.resources.map(({ prefix, suffix }) => {
                return this.http.get(`${prefix}${lang}${suffix}`);
            })
        ).pipe(
            map(response => {
                return response.reduce((a, b) => {
                    return Object.assign(a, b);
                });
            })
        );
    }
}

export function createTranslateLoader(http: HttpClient) {
    // TODO: ここでアプリIDをSessionStorageから取得する想定
    const appId = 'app-01';
    console.log('Translateのインスタンス生成');
    return new MultiTranslateHttpLoader(http, [
        // 複数アプリで共通の多言語ファイル
        { prefix: '/assets/language/common/', suffix: '.json' },
        // アプリごとの多言語ファイル
        { prefix: `/assets/language/${appId}/`, suffix: '.json' },
    ]);
}
```

## 実装したLoaderを`useFactory`に追加
```diff:app.component.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
+ import { createTranslateLoader } from './core/translate/multi-translate-http-loader';

@NgModule({
    declarations: [AppComponent],
    imports: [
        BrowserModule,
        AppRoutingModule,
        CoreModule,
        CoreModule,
        TranslateModule.forRoot({
            loader: {
                provide: TranslateLoader,
+               useFactory: createTranslateLoader,
                deps: [HttpClient],
            },
            isolate: false,
        }),
    ],
    providers: [],
    bootstrap: [AppComponent],
})
export class AppModule {}
```

## `app.component.ts`で言語の設定を書く
```diff:app.component.ts
import { Component } from '@angular/core';
+ import { TranslateService } from '@ngx-translate/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss'],
})
export class AppComponent {

    constructor(private translate: TranslateService) {
+       this.translate.setDefaultLang('en');
+       this.translate.use('en');
    }
}
```

## 多言語ファイルを用意する
ディレクトリの構成はこんな感じ。

```bash
src
└ assets
　 └ language
　 　 ├ app-01
　 　 │ └ en.json
　 　 ├ app-02
　 　 │ └ en.json
　 　 └ common
　 　 　 └ en.json
```

```json:app-01/en.json
{
  "おはよう これはapp01だよ": "Good morning this is app01"
}
```

```json:app-02/en.json
{
  "こんにちは これはapp02だよ": "Hello It's app02"
}
```

```json:common/en.json
{
  "これは共通だお": "This is common"
}
```

ちなみに表示する画面はこうだ。
「これは共通だお」はどちらのアプリIDでも翻訳される、残りはアプリIDによって翻訳されるされないが切り替わることが期待される動作。

```html:translate-example.component.html
<p translate>これは共通だお</p>
<p translate>おはよう これはapp01だよ</p>
<p translate>こんにちは これはapp02だよ</p>
```

## 確認してみる
まずはアプリIDが`app-01`の場合
![スクリーンショット 2019-12-29 2.09.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316318/a9f7f8c6-75db-bf1d-5cc4-658c7c272b1d.png)

次にアプリIDが`app-02`の場合
![スクリーンショット 2019-12-29 2.10.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316318/6ac4447f-3028-ffa6-7ab8-c1e755d4a836.png)

アプリIDによって参照される翻訳ファイルが切り替わることが確認できる。

## 参考
- [Loading Multiple file from a directory.](https://github.com/ngx-translate/core/issues/199#issuecomment-339084987)