Angular에서 간단하게 번역기능 구현하기 이글은 Angular에서 번역을 구현하는 방법에 대해 설명합니다.
소개 이 글에서 다음과 같은 방법을 배웁니다.
아래와 같이 HTML 뷰에서 단어를 번역하는데 사용할 수있는 Pipe 를 만듭니다.
1
2
<p > {{ 'hello world' | translate }}</p >
아래와 같이 JS/Typescript로 단어를 번역할 때 사용할 수 있는 Service 를 만듭니다.
1
2
3
4
...
this .translatedText = this .translate.instant('hello world' );
...
여러 번역 (예 : 영어, 스페인어, 중국어 등)을 정의한 다음 현재 언어 를 설정할 수 있습니다.
1
2
3
...
this .translate.use('es' );
...
번역을 위한 Placeholder 사용할 수 있습니다. 예를 들어, 아래와 같이 번역하고 싶다고 가정 해보십시오.
1
Hello, [first_name] [last name]!
다음과 같이 변환 값을 정의할 수 있어야합니다.1
2
3
4
...
'hello greet' : 'Hello %0 %1'
...
우리는 다음과 같이 translate Service 를 사용할 수 있기를 원합니다.1
this .translate.instant('hello greet' , [customer.firstName, customer.lastName]);
HTML View도 마찬가지입니다. 매개 변수를 전달할 수 있어야합니다.1
<p > {{ 'hello greet' | translate: [customer.firstName, customer.lastName] }}</p >
새로 고침 텍스트와 같은 기능을 실행하기 위해 언어가 변경 될 때마다 Publish하고 Subscribe합니다.
기본 언어를 사용하고 대비책을 사용하도록 설정합니다. 예를 들어 주어진 사용자가 현재 언어를 ‘중국어’로, 기본 언어를 ‘영어’로 설정합니다. 하지만 중국어에서 ‘hello’라는 단어를 찾을 수 없으면 영어 번역 정의에서 대체 단어를 찾아야합니다.
아래는 우리가 만들 UI입니다.
요구사항
사용자가 언어 버튼을 클릭하면 그에 따라 번역이 업데이트되어야합니다.
App 설정 다음은 파일 구조입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|- 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
설명
모든 translate
관련 파일을 위해 app 폴더 아래에 translate 폴더를 만들었습니다.
lang-[name].ts
파일은 번역 정의를 보관하는 곳입니다. 이 예에서는 영어(en), 스페인어(es) 및 중국어(zh)를 사용합니다.
translations.ts
는 모든 번역을 연결하는 곳입니다.
translate.pipe.ts
와 translate.service.ts
는 우리 Service 와 Pipe 의 파일입니다.
index.ts
는 Barrel로 export하기 위한 파일입니다. 나중에 Barrel에 대한 자세히 알아보겠습니다.
번역 정의 추가 lang-[name] 형식의 파일이름에 번역을 추가하겠습니다.
1
2
3
4
5
6
7
export const LANG_EN_NAME = 'en' ;
export const LANG_EN_TRANS = {
'hello world' : 'hello world' ,
};
1
2
3
4
5
6
7
export const LANG_ES_NAME = 'es' ;
export const LANG_ES_TRANS = {
'hello world' : 'hola mundo' ,
};
1
2
3
4
5
6
7
export const LANG_ZH_NAME = 'zh' ;
export const LANG_ZH_TRANS = {
'hello world' : '你好,世界' ,
};
번역 이제 translation.ts 파일의 모든 번역 파일을 연결합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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' ;
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,
};
export const TRANSLATION_PROVIDERS = [
{ provide: TRANSLATIONS, useValue: dictionary },
];
설명
모든 번역 정의 파일을 가져 왔습니다.
우리는 translations
라는 Opaque 토큰 을 만들었습니다. Opaque 토큰은 응용 프로그램 인터페이스가 없는 오브젝트입니다. 이것은 의존성 주입에 사용되는 특별한 종류의 Provider lookup key입니다. 자세한 내용은 Angular 공식 문서 를 참조하십시오.
dictionary
변수는 모든 번역을 링크합니다.
TRANSLATION_PROVIDERS
는 앞에서 정의한 Opaque 토큰을 사용하고 dictionary
을 값으로 제공한다고 설명합니다. 나중에 부트스트랩 (main.ts)에 TRANSLATION_PROVIDERS
를 등록할 것입니다.
Translate Service Service 를 만들어 봅시다.
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
// 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);
}
}
설명
TRANSLATIONS
토큰을 가져 와서 생성자에 주입합니다.
Translate Pipe 이제 Translate Pipe 를 만듭니다. Pipe 는 간단합니다 - Pipe 에 로직이 없습니다. 번역을 수행하기 위해 translate
Service 를 import하고 호출할 것입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Pipe, PipeTransform } from '@angular/core' ;
import { TranslateService } from '../translate' ;
@Pipe ({
name: 'translate' ,
})
export class TranslatePipe implements PipeTransform {
constructor (private _translate: TranslateService ) { }
transform(value: string , args: any []): any {
if (!value) return ;
return this ._translate.instant(value);
}
}
번역 사용하기 Translate Service 와 Pipe 가 모두 완료되었습니다. App Component 에서 사용해 봅시다.
App Module로 Import 하기 Translate Service 와 Pipe 를 사용하려면 응용 프로그램 모듈로 Import 해야합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 ],
bootstrap: [ AppComponent ],
providers : [ TRANSLATION_PROVIDERS, TranslateService ]
})
export class AppModule { }
설명
응용프로그램 레벨에서 Service 와 Pipe 를 등록합니다.
Translate Pipe 를 global하게 (응용 프로그램 전체에서) 사용할 수 있도록합니다. 이 포스트 를 참고하세요.
App Component 이제 Component 에서 사용할 수 있습니다.
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
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() {
this .supportedLangs = [
{ display: 'English' , value: 'en' },
{ display: 'Español' , value: 'es' },
{ display: '华语' , value: 'zh' },
];
this .selectLang('es' );
}
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' );
}
}
설명
지원되는 모든 언어를 저장하는 supportedLanguages
배열을 사용합니다.
기본적으로 언어는 스페인어로 설정됩니다. 물론 사용자의 브라우저 언어 navigator.language
를 읽고 그에 따라 기본 언어를 설정할 수 있습니다.
새로운 언어가 선택되면 우리는 translatedText
를 새로 고칩니다. (이어지는 내용에서 새로 고침을 처리하는 방법을 향상시킬 것입니다).
App HTML 뷰 다음은 HTML 뷰입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<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 >
<p >
Translate With Pipe: <strong > {{ 'hello world' | translate }}</strong >
</p >
<p >
Translate with Service: <strong > {{ translatedText }}</strong >
</p >
</div >
</div >
배럴(Barrel)로 묶기 배럴(barrel)로 Export 하기 translation 모듈을 배럴로 Export할 것입니다. 배럴은 여러 모듈의 Export를 단일 모듈로 감싸는 방법입니다. 배럴 자체는 다른 모듈의 선택된 Export를 다시 Export하는 모듈 파일입니다. 자세한 내용은 Angular 공식 문서 를 참조하십시오.
1
2
3
4
5
export * from './translate.service' ;
export * from './translations' ;
export * from './translate.pipe' ;
부트스트랩(Bootstrap) 이제 응용 프로그램을 부트스트랩 합니다.
1
2
3
4
5
6
7
8
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' ;
import { AppModule } from './app.module' ;
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
시스템 설정 systemjs를 사용하고 Angular CLI를 프로젝트 설정에 사용하는 경우 translate barrel 을 system-config.ts 파일에 포함합니다.
1
2
3
4
5
6
7
'app' ,
'app/translate' ,
'app/shared' ,
거의 끝났습니다. 하지만.. 애플리케이션을 실행해 봅시다. 응용 프로그램이 로드되면 스페인어가 선택되고 번역은 예상대로 hola mundo
를 표시합니다. 영어를 선택해 보십시오. 하지만 Translate Pipe 값이 업데이트되지 않습니다.
왜 그럴까요? Angular 2 Pipe 는 기본적으로 Pure 합니다. Angular는 입력값에 대한 순수한 변경을 감지한 경우에만 Pure Pipe 를 실행합니다. 우리의 경우, 입력값이 변경되지 않아 여전히 Hello world입니다. 따라서 값이 업데이트되지 않습니다.
해결책 우리 Pipe 를 impure 로 만듭니다. Angular는 모든 Component 변경 감지 사이클 동안 Impure Pipe 를 실행합니다. 이 경우 언어 선택을 업데이트하면 변경 사항이 트리거되고 Impure Pipe 가 그에 따라 업데이트됩니다.
우리 Pipe 를 Impure로 만들려면 app/translate/translate.pipe.ts
를 열고 다음 한줄을 추가하십시오.
1
2
3
4
5
6
7
8
9
...
@Pipe ({
name: 'translate' ,
pure: false
})
...
Pipe 에 대한 자세한 내용은 Angular 2 Pipes 문서 를 참조하십시오.
Placeholder 사용을 위한 번역 정의 추가 먼저 번역 정의 파일에 번역을 추가해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
export const LANG_EN_NAME = 'en' ;
export const LANG_EN_TRANS = {
'hello world' : 'hello world' ,
'hello greet' : 'Hello, %0 %1!' ,
'well done' : 'Well done %0' ,
'good bye' : 'bye bye' ,
};
1
2
3
4
5
6
7
8
9
export const LANG_ES_NAME = 'es' ;
export const LANG_ES_TRANS = {
'hello world' : 'hola mundo' ,
'hello greet' : 'Hola, %0 %1!' ,
'well done' : '%0 bien hecho' ,
};
1
2
3
4
5
6
7
8
9
export const LANG_ZH_NAME = 'zh' ;
export const LANG_ZH_TRANS = {
'hello world' : '你好,世界' ,
'hello greet' : '你好, %0 %1!' ,
'well done' : '干得好, %0' ,
};
번역에서 Placeholder 사용 이 작업은 꽤 쉽습니다. translate.service.ts
의 instant
함수를 수정하여 선택적 파라미터를 받아 들이도록 하면 됩니다. 파라미터가 문자열 또는 문자열 배열을 허용할 수 있도록 하면 더 좋습니다.
Translate Service 수정하기 1
2
3
4
5
6
7
8
9
10
11
...
public instant(key: string , words?: string | string []) {
const translation: string = this .translate(key);
if (!words) return translation;
return this .replace(translation, words);
}
...
이제 replace
함수를 구현해야합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
private PLACEHOLDER = '%' ;
public replace(word: string = '' , words: string | string [] = '' ) {
let translation: string = word;
const values: string [] = [].concat(words);
values.forEach((e, i ) => {
translation = translation.replace(this .PLACEHOLDER.concat(<any >i), e);
});
return translation;
}
...
Translate Pipe 변경 Pipe 도 업데이트 해야합니다.
1
2
3
4
5
6
7
8
...
transform(value: string , args: string | string []): any {
if (!value) return ;
return this ._translate.instant(value, args);
}
...
HTML 뷰에서 사용 이제 번역을 사용하기 위해 HTML 뷰를 업데이트 해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<p >
Translate <strong class ="text-muted" > Hello, %0 %1!</strong > :
<br >
<strong > {{ 'hello greet' | translate:['Jane', 'Doe'] }}</strong >
</p >
<p >
Translate <strong class ="text-muted" > Well done %0</strong > :
<br >
<strong > {{ 'well done' | translate:'John' }}</strong >
</p >
언어 변경 Event를 Publish하고 Subscribe 하기 translate.service.ts
파일에 Event 발생기능을 추가합니다.
Translate Service 변경하기 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
import { Injectable, Inject, EventEmitter } from '@angular/core' ;
...
public onLangChanged: EventEmitter<string > = new EventEmitter<string >();
..
public use(lang: string ): void {
...
this .onLangChanged.emit(lang);
}
...
App Component에서 사용하기 이제 App Component에서 사용할 수 있습니다. 앞서 논의한 selectLang()
에서 refreshText()
를 제거하기 위한 코드를 리팩토링 합니다.
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
...
ngOnInit() {
...
this .subscribeToLangChanged();
this .selectLang('es' );
}
selectLang(lang: string ) {
this ._translate.use(lang);
}
subscribeToLangChanged() {
return this ._translate.onLangChanged.subscribe(x => this .refreshText());
}
...
이제 언어가 변경될 때마다 번역이 계속 업데이트됩니다.
기본언어 사용 및 대체언어 사용 새로운 속성을 추가하고 translate.service.ts
의 translate
함수를 리팩토링 합니다.
Translate Service 변경하기 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
...
private _defaultLang: string ;
private _currentLang: string ;
private _fallback: boolean ;
public get currentLang() {
return this ._currentLang || this ._defaultLang;
}
public setDefaultLang(lang: string ) {
this ._defaultLang = lang;
}
public enableFallback(enable: boolean ) {
this ._fallback = enable;
}
private translate(key: string ): string {
let translation = key;
if (this ._translations[this .currentLang] && this ._translations[this .currentLang][key]) {
return this ._translations[this .currentLang][key];
}
if (!this ._fallback) {
return translation;
}
if (this ._translations[this ._defaultLang] && this ._translations[this ._defaultLang][key]) {
return this ._translations[this ._defaultLang][key];
}
return translation;
}
...
App Component에서 사용하기 ngOnInit()
에서 기본언어와 대체언어를 설정할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
ngOnInit() {
...
this .subscribeToLangChanged();
this ._translate.setDefaultLang('en' );
this ._translate.enableFallback(true );
this .selectLang('es' );
}
...
HTML 뷰에서 사용 HTML에서 테스트해 봅시다. 이전에 번역 정의 추가 단계에서 영어 번역을 위해 good bye 를 추가했지만 스페인어와 중국어로 추가하지 않았습니다. 사용자가 스페인어 또는 중국어를 선택한 경우 디스플레이는 bye bye
입니다. 왜냐하면 우리는 컴포너트에서 대체 언어 사용을 설정했기 때문입니다.
1
2
3
4
5
<p >
Translate <strong class ="text-muted" > Good bye (fallback)</strong > :
<br >
<strong > {{ 'good bye' | translate }}</strong >
</p >
요약 이게 전부입니다. Service 및 Pipe 와 대체언어, Event Publish/Subscribe 및 기본 언어와 같은 고급 기능을 사용하여 기본적인 번역 설정을 설명했습니다.
이 내용은 나중에 참고하기 위해 제가 공부하며 정리한 내용입니다. 의역, 오역, 직역이 있을 수 있음을 알려드립니다. This post is a translation of this original article [https://scotch.io/tutorials/simple-language-translation-in-angular-2-part-1 , https://scotch.io/tutorials/simple-language-translation-in-angular-2-part-2 ]
참고