iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 12
1

前言

一個小網站,客群可能很單一,在台灣也許只要網站只要有中文就好了。可是隨著服務擴大,客群也跟著變大,可能產品進軍亞洲,甚至拓展到全球,這時候至少會提供英文介面,甚至日文、韓文都有能。當我們點進大公司的網站時,也都可以看到他們提供好幾種語言,可見多語言的介面非常重要。

目標

模板可以這樣使用

<!-- 當我們翻譯成西班牙語,應該變成 'hola mundo'  -->
<p>{{ 'hello world' | translate }}</p>

模組的部分大概會長這樣

this.translate.use('es'); // 用西班牙文

關於翻譯技術層面的新知識並沒有太多,主要是將一些基礎概念作應用,因此接下來比較不會有太多文字解釋,主要還是以程式碼來展現如何實現我們的目標。

Plunker

藍圖

|- app/
    |- app.component.html
    |- app.component.ts
    |- app.module.ts
    |- main.ts
    |- translate/
        |- index.ts
        |- lang-en.ts
        |- lang-es.ts
        |- lang-zh.ts
        |- translate.pipe.ts
        |- translate.service.ts
        |- translation.ts
|- index.html
|- systemjs.config.js
|- tsconfig.json

動工

首先要連結我們的字典檔

// app/translate/translation.ts

import { OpaqueToken } from '@angular/core';

// 引進我們的語言檔案包
import { LANG_EN_NAME, LANG_EN_TRANS } from './lang-en';
import { LANG_ES_NAME, LANG_ES_TRANS } from './lang-es';
import { LANG_ZH_NAME, LANG_ZH_TRANS } from './lang-zh';

// translation token
export const TRANSLATIONS = new OpaqueToken('translations');

// 翻譯辭典
const dictionary = {
    [LANG_EN_NAME]: LANG_EN_TRANS,
    [LANG_ES_NAME]: LANG_ES_TRANS,
    [LANG_ZH_NAME]: LANG_ZH_TRANS,
};

// providers
export const TRANSLATION_PROVIDERS = [
    { provide: TRANSLATIONS, useValue: dictionary },
];

TRANSLATION_PROVIDERS 會註冊在一開始的 bootstrap。

Translate Service

接著要建立服務,用來做切換語言用

// app/translate/translate.service.ts

import {Injectable, Inject} from '@angular/core';
import { TRANSLATIONS } from './translations'; // import our opaque token

@Injectable()
export class TranslateService {
    private _currentLang: string;

    public get currentLang() {
        return this._currentLang;
    }

    // inject our translations
    constructor(@Inject(TRANSLATIONS) private _translations: any) {
    }

    public use(lang: string): void {
        // set current language
        this._currentLang = lang;
    }

    private translate(key: string): string {
        // private perform translation
        let translation = key;

        if (this._translations[this.currentLang] && this._translations[this.currentLang][key]) {
            return this._translations[this.currentLang][key];
        }

        return translation;
    }

    public instant(key: string) {
        // call translation
        return this.translate(key); 
    }
}

Translation Pipe

之前介紹過 Pipe,目的是讓我們在模板中的文字,能透過 Pipe 來做切換。

// app/translate/translate.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';
import { TranslateService } from '../translate'; // translate service

@Pipe({
    name: 'translate',
    pure: false // 使成為 impure
})

export class TranslatePipe implements PipeTransform {

    constructor(private _translate: TranslateService) { }

    transform(value: string, args: any[]): any {
        if (!value) return;
        return this._translate.instant(value);
    }
}

@Pipe 裡面的 pure 要選 false。這樣才會變成 impure pipe。 Angular 在每次偵測變化的週期都會執行 impure pipe。如果是 pure pipe,只有在 Input 有變化才會執行。 所以這邊我們要加上這句,才能順利讓程式翻譯。

字典檔

簡單來說就是放一堆語詞的模組。

// lang-zh.ts

export const LANG_ZH_NAME = 'zh';

export const LANG_ZH_TRANS = {
    'hello world': '你好,世界',
};

App Module

基礎設置,將用到的東西引入,並將 TranslatePipe 注入。

// app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }   from './app.component';
import { TRANSLATION_PROVIDERS, TranslatePipe, TranslateService }   from './translate';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, TranslatePipe ], // Inject Translate Pipe here
  bootstrap:    [ AppComponent ],
  providers:    [ TRANSLATION_PROVIDERS, TranslateService ]
})

export class AppModule { }

App Component

// app/app.component.ts

import { Component, OnInit } from '@angular/core';
import { TranslateService } from './translate';

@Component({
    moduleId: module.id,
    selector: 'app-root',
    templateUrl: 'app.component.html',
})

export class AppComponent implements OnInit {

    public translatedText: string;
    public supportedLanguages: any[];

    constructor(private _translate: TranslateService) { }

    ngOnInit() {
        // standing data
        this.supportedLangs = [
        { display: 'English', value: 'en' },
        { display: 'Español', value: 'es' },
        { display: '中文', value: 'zh' },
        ];

        // 設定目前語言
        this.selectLang('zh');
    }

    isCurrentLang(lang: string) {
        //確定選的語言是否為顯示語言
        return lang === this._translate.currentLang;
    }

    selectLang(lang: string) {
        // 設定目前語言
        this._translate.use(lang);
        this.refreshText();
    }

    refreshText() {
        // 語言改變就刷新
        this.translatedText = this._translate.instant('hello world');
    }
}

App Template

這邊我們用兩種方式翻譯,一個是使用 Pipe,一個使用服務。

<!--app/app.component.html-->

<div class="container">
  <h4>Translate: Hello World</h4>
  <div class="btn-group">
    <button *ngFor="let lang of supportedLangs"
      (click)="selectLang(lang.value)"
      class="btn btn-default" [class.btn-primary]="isCurrentLang(lang.value)">
      {{ lang.display }}
    </button>
  </div>
  <div style="margin-top: 20px">
    <p>
      使用 Pipe 翻譯: <strong>{{ 'hello world' | translate }}</strong>
    </p>
    <p>
      使用 Service 翻譯: <strong>{{ translatedText }}</strong>
    </p>
  </div>
</div>

上一篇
[Day 11] Angular 2 HTTP 方法--讓我 Call API
下一篇
[Day 13] Angular 2 多語言 ( 2 )
系列文
Angular 2 之 30 天邁向神乎其技之路31

尚未有邦友留言

立即登入留言