【Angular】ngx-translate で多言語対応

Uncategorized
1k words

メモです。

環境

  • Windows 11 Home 21H2 22000.556
  • Angular CLI: 13.2.6
  • Node: 16.13.1
  • Package Manager: npm 8.3.0
  • @ngx-translate/core 14.0.0
  • @ngx-translate/http-loader 7.0.0

準備

お試しということで、サンプルアプリ に ローカライゼーション を実装していきます。

toh-pt6

まず ZIP をダウンロードして、展開して、デバッグ実行ができる状態まで進めておきます。

Tour of Heroes

手順

インストール

次のコマンドで npmモジュール をインストールします。

1
2
npm install @ngx-translate/core --save
npm install @ngx-translate/http-loader --save

実装

「src\app\app.module.ts」を次のように修正します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroSearchComponent } from './hero-search/hero-search.component';
import { MessagesComponent } from './messages/messages.component';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';

export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

@NgModule({
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: createTranslateLoader,
deps: [HttpClient]
},
defaultLanguage: 'en'
}),

// HttpClientInMemoryWebApiModule.forRoot(
// InMemoryDataService, { dataEncapsulation: false }
// )
],
declarations: [
AppComponent,
DashboardComponent,
HeroesComponent,
HeroDetailComponent,
MessagesComponent,
HeroSearchComponent
],
providers: [],
bootstrap: [AppComponent]
})

export class AppModule {
constructor(translate: TranslateService) {
translate.use('ja');
}
}

app.module.ts

翻訳の設定

後は翻訳の設定を書いていくだけです。

「src\assets\i18n\」に、読み込む翻訳JSONを作成します。

今回は「ja.json」と「en.json」をそれぞれ作りました。

1
2
3
4
5
6
7
{
"Dashboard": "ダッシュボード",
"Heroes": "ヒーローズ",
"Tour of Heroes": "ツアー・オブ・ヒーローズ",
"Top Heroes": "上位ヒーローズ",
"Hero Search": "ヒーロー検索"
}

ja.json

HTML内に翻訳を埋め込む場合は、次ように修正します。

1
2
3
4
5
6
7
<h1>{{title}}</h1>
<nav>
<a routerLink="/dashboard" translate>{{'Dashboard'}}</a>
<a routerLink="/heroes" translate>{{'Heroes'}}</a>
</nav>
<router-outlet></router-outlet>
<app-messages></app-messages>

app.component.html

TSから翻訳する場合は、次のように修正します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

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

constructor(private translate: TranslateService) {
this.translate.get('Tour of Heroes').subscribe((res: string) => {
this.title = res;
});
}
}

app.component.ts

確認

上記修正をすると、次のように表示されます。

ツアー・オブ・ヒーローズ

キャッシュバスティング

翻訳JSONをキャッシュバスティングに対応させます。

「src\app\app.module.ts」を次のように修正することで、ページを開くと新たな翻訳JSONを取得するようになります。

1
2
3
4
5
6
7
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
↓↓↓
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json?cb=' + new Date().getTime());
}

'./assets/i18n/', '.json?cb=' + new Date().getTime()

実行してみるとこんな感じに、毎回取得しているのが分かると思います。

キャッシュバスティング

理想を言えばビルドやデプロイ毎で、キャッシュバスティングされればいいのですが、Angular にはその機能がないようです。

今後のアップデートに期待します。

躓いた点

./assets/i18n/en.json 404 Not Found

次のエラーが出て、翻訳JSONが取得できなかった。

1
ERROR {body: {…}, url: './assets/i18n/en.json', headers: HttpHeaders, status: 404, statusText: 'Not Found'}

ERROR {body: {…}, url: './assets/i18n/en.json', headers: HttpHeaders, status: 404, statusText: 'Not Found'}

解決

「src\app\app.module.ts」内で HttpClientInMemoryWebApiModule が読み込まれているとエラーになるみたいなので、コメントアウトしました。

HttpClientInMemoryWebApiModule をコメントアウト

Invalid version: “15.2-15.3”

次のエラーが出て、build ができなかった。

1
2
3
4
5
6
7
8
9
10
11
12
PS D:\Angular\toh-pt6> npm run build

> angular.io-example@0.0.0 build
> ng build

⠙ Generating browser application bundles (phase: setup)...Processing legacy "View Engine" libraries:
- angular-in-memory-web-api [module/esm5] (git+https://github.com/angular/in-memory-web-api.git)
Encourage the library authors to publish an Ivy distribution.
✔ Browser application bundle generation complete.
An unhandled exception occurred: Transform failed with 1 error:
error: Invalid version: "15.2-15.3"
See "C:\Users*****\AppData\Local\Temp\ng-jJmYje\angular-errors.log" for further details.

error: Invalid version: "15.2-15.3"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[error] HookWebpackError: Transform failed with 1 error:
error: Invalid version: "15.2-15.3"
at makeWebpackError (D:\Angular\toh-pt6\node_modules\webpack\lib\HookWebpackError.js:48:9)
at D:\Angular\toh-pt6\node_modules\webpack\lib\Compilation.js:3055:12
at eval (eval at create (D:\Angular\toh-pt6\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:98:1)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
-- inner error --
Error: Transform failed with 1 error:
error: Invalid version: "15.2-15.3"
at failureErrorWithLog (D:\Angular\toh-pt6\node_modules\esbuild\lib\main.js:1557:15)
at D:\Angular\toh-pt6\node_modules\esbuild\lib\main.js:1346:29
at D:\Angular\toh-pt6\node_modules\esbuild\lib\main.js:637:9
at handleIncomingPacket (D:\Angular\toh-pt6\node_modules\esbuild\lib\main.js:734:9)
at Socket.readFromStdout (D:\Angular\toh-pt6\node_modules\esbuild\lib\main.js:604:7)
at Socket.emit (node:events:390:28)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at Socket.Readable.push (node:internal/streams/readable:228:10)
at Pipe.onStreamRead (node:internal/stream_base_commons:199:23)

解決

「@angular-devkit/build-angular」を 13.2.6 にアップデートしたらビルドできた。

1
npm update @angular-devkit/build-angular@13.2.6

npm run build

参考

https://github.com/ngx-translate/core