Skip to content

Commit

Permalink
feat(module:theme:i18n): add i18n pipe (#238)
Browse files Browse the repository at this point in the history
  • Loading branch information
cipchk committed Oct 28, 2018
1 parent 0298665 commit 423c9c3
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 9 deletions.
10 changes: 8 additions & 2 deletions docs/i18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ switchLanguage() {
}
```

### ALAIN_I18N_TOKEN
## ALAIN_I18N_TOKEN

`@delon/*` 类库有许多带有 _i18n_ 字样的数据接口属性(例如:`page-header``st` 列描述、`Menu` 菜单数据等等),当你希望这些组件的数据接口能动态根据 Key 值按当前语言自动切换时,你还需要对 `ALAIN_I18N_TOKEN` 定义一个自实现服务接口(例如:[I18NService](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/i18n/i18n.service.ts)),并在根模块下注册。

Expand All @@ -104,6 +104,12 @@ import { I18NService } from '@core/i18n/i18n.service';
export class AppModule {}
```

### i18n管道

为了不受第三方各自管道的命名方式,脚手架包含一个 `i18n` 的管道,它相当于直接调用 `ALAIN_I18N_TOKEN``fanyi` 方法。

他和 `@ngx-translate``| translate` 有所不同,它会监听语言的变化并自动更新。而 `| i18n` 不会监听语言变更通知所以会有更好的性能,当你明确在切换语言后会重新渲染 Angular 项目时 `| i18n` 会更适合。

## 如何添加

创建脚手架[命令行](/cli/add) `ng add ng-alain@next` 时允许指定 `--i18n` 表示是否包含国际化示例代码(采用 `@ngx-translate/core` 方式)。
Expand All @@ -114,7 +120,7 @@ export class AppModule {}

- `src/app/core/i18n` 目录
- `app.module.ts``TranslateModule` 相关声明
- 替换掉默认布局可能出现的 I18n 的 Pipe 使用 `| translate`
- 替换掉默认布局可能出现的 I18n 的 Pipe 使用 `| translate``| i18n`
- 移除 `@ngx-translate/core``@ngx-translate/http-loader` 包体

## 默认语言
Expand Down
11 changes: 11 additions & 0 deletions packages/theme/src/services/i18n/i18n.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { PipeTransform, Pipe, Inject } from '@angular/core';
import { ALAIN_I18N_TOKEN, AlainI18NService } from './i18n';

@Pipe({ name: 'i18n' })
export class I18nPipe implements PipeTransform {
constructor(@Inject(ALAIN_I18N_TOKEN) private i18n: AlainI18NService) {}

transform(key: string, interpolateParams?: Object, isSafe?: boolean): string {
return this.i18n.fanyi(key, interpolateParams, isSafe);
}
}
96 changes: 95 additions & 1 deletion packages/theme/src/services/i18n/i18n.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { en_US } from 'ng-zorro-antd';
import { AlainI18NServiceFake } from './i18n';
import {
AlainI18NServiceFake,
AlainI18NService,
ALAIN_I18N_TOKEN,
} from './i18n';
import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AlainThemeModule } from '../../theme.module';
import { By } from '@angular/platform-browser';

describe('theme: i18n', () => {
const i18n = new AlainI18NServiceFake();
Expand All @@ -16,4 +24,90 @@ describe('theme: i18n', () => {
it('#fanyi', () => {
expect(i18n.fanyi('index')).toBe('index');
});

describe('[i18n pipe]', () => {
let fixture: ComponentFixture<TestComponent>;
let srv: AlainI18NService;

class MockI18NService extends AlainI18NServiceFake {
data: any = {};
use(lang: string) {
this.data = {
simple: 'a',
param: 'a-{{value}}',
html: '<i>asdf</i>',
};
}
fanyi(key: string, data?: Object, isSafe?: boolean) {
let res = this.data[key] || '';
if (data) {
Object.keys(data).forEach(
key =>
(res = res.replace(new RegExp(`{{${key}}}`, 'g'), data[key])),
);
}
return res;
}
}

function genModule() {
TestBed.configureTestingModule({
imports: [AlainThemeModule.forRoot()],
declarations: [TestComponent],
providers: [
{
provide: ALAIN_I18N_TOKEN,
useClass: MockI18NService,
multi: false,
},
],
});
fixture = TestBed.createComponent(TestComponent);
srv = fixture.debugElement.injector.get(ALAIN_I18N_TOKEN);
srv.use('en');
fixture.detectChanges();
}

function check(result: string, id = 'simple') {
const el = fixture.debugElement.query(By.css('#' + id))
.nativeElement as HTMLElement;

expect(el.textContent.trim()).toBe(result);
}

it('should working', () => {
genModule();
check('a');
});

it('should be param', () => {
genModule();
fixture.componentInstance.key = 'param';
fixture.componentInstance.params = { value: '1' };
fixture.detectChanges();
check('a-1', 'param');
});

it('should be safeHtml', () => {
genModule();
fixture.componentInstance.key = 'html';
fixture.componentInstance.params = { value: '1' };
fixture.componentInstance.isSafe = true;
fixture.detectChanges();
check('asdf', 'html');
});
});
});

@Component({
template: `
<div id="simple">{{ key | i18n }}</div>
<div id="param">{{ key | i18n : params }}</div>
<div id="html" [innerHTML]="key | i18n : params : isSafe"></div>
`,
})
class TestComponent {
key = 'simple';
params = {};
isSafe = true;
}
22 changes: 19 additions & 3 deletions packages/theme/src/services/i18n/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,28 @@ import { filter } from 'rxjs/operators';
export interface AlainI18NService {
[key: string]: any;

use(lang: string): void;

/**
* 变更语言
* @param lang 语言代码
* @param emit 是否触发 `change`,默认:true
*/
use(lang: string, emit?: boolean): void;

/**
* 返回当前语言列表
*/
getLangs(): any[];

fanyi(key: string): any;
/**
* 翻译
* - `interpolateParams` 模板所需要的参数对象
* - `isSafe` 是否返回安全字符,自动调用 `bypassSecurityTrustHtml`
*/
fanyi(key: string, interpolateParams?: Object, isSafe?: boolean): string;

/**
* 调用 `use` 触发变更通知
*/
readonly change: Observable<string>;
}

Expand Down
3 changes: 2 additions & 1 deletion packages/theme/src/theme.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import { DatePipe } from './pipes/date/date.pipe';
import { CNCurrencyPipe } from './pipes/currency/cn-currency.pipe';
import { KeysPipe } from './pipes/keys/keys.pipe';
import { YNPipe } from './pipes/yn/yn.pipe';
import { I18nPipe } from './services/i18n/i18n.pipe';
import { HTMLPipe } from './pipes/html/html.pipe';
const PIPES = [DatePipe, CNCurrencyPipe, KeysPipe, YNPipe, HTMLPipe];
const PIPES = [DatePipe, CNCurrencyPipe, KeysPipe, YNPipe, I18nPipe, HTMLPipe];

// #endregion

Expand Down
10 changes: 8 additions & 2 deletions src/app/core/i18n/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Subject } from 'rxjs';
import { DomSanitizer } from '@angular/platform-browser';

import { en_US, zh_CN, NzI18nService } from 'ng-zorro-antd';
import {
Expand Down Expand Up @@ -28,6 +29,7 @@ export class I18NService implements AlainI18NService {
private zorroI18n: NzI18nService,
private delonI18n: DelonLocaleService,
private translate: TranslateService,
private dom: DomSanitizer,
) {
this.translate.setTranslation('en-US', ENUS);
this.translate.setTranslation('zh-CN', ZHCN);
Expand Down Expand Up @@ -92,8 +94,12 @@ export class I18NService implements AlainI18NService {
return ['zh-CN', 'en-US'];
}

fanyi(key: string) {
return this.translate.instant(key);
fanyi(key: string, interpolateParams?: Object, isSafe?: boolean) {
const res = this.translate.instant(key, interpolateParams);
if (isSafe === true) {
return this.dom.bypassSecurityTrustHtml(res);
}
return res;
}

get(i: any) {
Expand Down

0 comments on commit 423c9c3

Please sign in to comment.