良い一日。
近い将来、ngx-translateライフハックを公開する予定です。
- 最初の部分では、JSONファイル内でパイプをコンパイルする方法を教えるために
Translateompiler
を検証します。 - 2番目の部分では、
TranslateLoader
を置き換えるためにTranslateHttpLoader
を作成します(これはngx-translateボックスから出ませんが、それでも個別にインストールできるデフォルトのローダーです)。
TranslateLoader
は、まずブラウザー言語に注意を払い、フォールバックロジックを含み、MomentJsローカリゼーションをインポートし、APP_INITIALIZERを介してロードしAPP_INITIALIZER
。
また、ngx-translate json`ovをアプリケーションにロードする最初の方法を検討してください。 3番目の部分は実験的で、Webpackユーザーのライフハックです。 プロジェクトのngx-translateインフラストラクチャを改善し、translate jsonをバンドルで構築する方法を検討します。
始めましょう。
したがって、インターフェースにエンティティの作成日を追加のテキストとともに表示する必要があるとしましょう。
<p>This entity created at 15:47 01-02-2003 by Some Guy</p>
この行のen.jsonに新しいフィールドを追加します。
"ENTITY_CREATED_AT": "This entity created at {{createdAtDate}} by {{guyName}}"
コンポーネントのビューでは、当然、translateディレクティブまたはpipeを追加します
<div translate="ENTITY_CREATED_AT" [translateParams]="{createdAtDate: entity.createdAt, guyName: entity.name}"></div>
しかし、このアプローチでは問題が発生します-createdAtDateフィールドは既にローカライズされている必要があり、そうでない場合はnew Date().toString().
の結果が表示されnew Date().toString().
この問題には2aの解決策があります。
私はtranslateParamsの別のオブジェクトを転送して、jsonのフィールドを実際のモデルのフィールドに配置することを好みます。
トビッシュ:
//our Entity model class Entity { createdAt: Date; name: string; someComplexField: ComplexType; } //inside some HTML [translateParams]="{createdAtDate: entity.createdAt, guyName: entity.name}" // separate object
もちろん、jsonでは、エンティティモデルでフィールド名を明示的に指定することで、実際のモデルのフィールドを参照することができました。
"ENTITY_CREATED_AT": "This entity created at {{createdAt}} by {{name}}" // entity.createdAt, entity.name
translateParamsでは、単にエンティティを渡します。
<div translate="ENTITY_CREATED_AT" [translateParams]="entity"></div>
しかし、すでに10の言語をサポートしており、モデルがngx-translateに関連付けられているフィールドの1つの名前を突然変更した場合はどうなりますか?
ビューコンポーネント内のヘルプ機能。
ビューコンポーネント内に関数を作成し、translateParamsに転送するための既製のオブジェクトを返します。
public getEntityTranslateParam(dateFormat: string) { return { createdAtDate: moment.format(dateFormat, entity.createdAt), // guyName: entity.name }; }
欠点があります-そのような機能で各コンポーネントを詰まらせる必要があります。 もちろん、このようなオブジェクトを作成する特定のサービスを追加して、コンポーネントのコードを少しきれいにすることができます。このサービスはどこにでも注入する必要があるため、オブジェクトが十分に大きい場合にのみ意味があります。
ENTITY_CREATED_ATを2つの部分に分けます。
"ENTITY_CREATED_AT_1": "This entity created at", "ENTITY_CREATED_AT_2": "by {{guyName}}"
ここで私たちは成長し、HTMLを読むことが難しくなっていますが、今ではあらゆる種類のパイプを使用できます! (もちろん、パイプロケーションディレクティブを使用できますが、div内にさらにhtmlを作成する必要があります)
<div> {{'ENTITY_CREATED_AT_1' | translate}} {{entity.createdAt | dateFormatPipe:LL}} {{'ENTITY_CREATED_AT_2' | translate:{guyName:entity.guyName} }} </div>
優れたインフラストラクチャについて、ngx-translate開発者を称賛してください!
ngx-translateモジュールをアプリケーションにインポートするとき、カスタムTranslateompiler
を提供できます。
ところで、ここに優れた複数の\性別などへのリンクがあります。 ngx-translateで動作するコンパイラ。
私たちのタスクは、ngx-translateローカライズ内でパイプを実行できるTranslateompilerを作成することです。
まず、DIを準備し(Injector`aからパイプを取得するため)、ngx-translateを初期化することから始めましょう。
モジュールのプロバイダーに目的のパイプを追加します。私の場合、アプリケーションには複数のモジュールが含まれているため、これはSharedModule
です。
@NgModule({ // imports: [ ...exportedModules ], declarations: [ ...exportedDeclarations ], exports: [ ...exportedModules, ...exportedDeclarations ], providers: [ // declare pipes available with injector [ DateFormatPipe, {provide: 'dateFormat', useExisting: DateFormatPipe} ] ] }) export class SharedModule { }
ngx-translateを初期化し、 CustomTranslateCompiler
をポイントします。
私の場合、 CoreModule
を使用していCoreModule
。
import { CustomTranslateCompiler } from 'app/_core/services/translate/translate.compiler'; @NgModule({ // some needed providers imports: [ CommonModule, TranslateModule.forRoot({ compiler: { provide: TranslateCompiler, useClass: CustomTranslateCompiler, deps: [Injector] }, }) ], exports: [CommonModule, TranslateModule] }) export class CoreModule { }
モジュールのプロバイダーで宣言されているパイプのみを使用できます(パイプの例-dateFormst)。
"ENTITY_CREATED_AT": "This entity created at {{createdAtDate | dateFormat:LL}} by {{guyName}}"
デフォルトでPipeTranslateCompiler
定義しましょう。
export class PipeTranslateCompiler implements TranslateCompiler { constructor(private injector: Injector, private errorHandler: ErrorHandler) { } public compile(value: string, lang: string): string | Function { return value; } public compileTranslations(translations: any, lang: string): any { return translations; } }
2つの機能があります。
compileTranslations-入力時にロードされたjsonを取得します(TranslateHttpLoaderからダウンロードできます)。
compile-1つの値のみを取得し、 TranslateService.set('some translate val', 'key', 'en')
介してngx-translateに明示的にフィールドを追加すると呼び出されます。
十分に大きいjsonはcompileTranslationsに入る可能性があるため、オブジェクト全体のフィールドを再帰的に調べて各値を解析する必要があります。どのフィールドを解析し、どのフィールドを解析しないかを事前に知りたいです。 したがって、PipeTranslateCompilerで保存する必要があるすべてのフィールドを@でマークすることにしました。
"@ENTITY_CREATED_AT": "This entity created at {{createdAtDate | dateFormat:LL}} by {{guyName}}", // "OTHER_KEY": "This is regular entity" //
では、 compile - string | Function
戻り値の型に注意しましょうcompile - string | Function
compile - string | Function
、これは、任意の文字列値を関数に変換できることを意味します.HTMLビューでngx-translateの変換パイプまたはディレクティブを使用すると、関数が何らかのキーに配置されている場合、パラメーターで呼び出します。 translateParamsに渡します。
<div>{{'ENTITY_CREATED_AT' | translate:paramsObject}}</div> // ENTITY_CREATED_AT - transform paramsObject.
そのため、 "This entity created at {{createdAtDate | dateFormat:LL}} by {{guyName}}"
という文字列を解析し、関数で置き換える必要があります。
最初に、文字列の解析結果を保存する方法を想像してみましょう。これにより、後でパラメーターに基づいて、既に適用されたパイプを含む文字列をすばやく取得できます。
2+個のパラメーターを持つ2+個のパイプをすぐに解析できるようにしたい
"SOME_KEY": "value1: {{dateField | dateFormat:LL}} and value2: {{anotherField | customPipe:param1:param2}}
そのため、括弧{{。*}}内の部分を強調表示して、文字列を正規表現で解析する必要があります。
強調表示されたブラケットごとに、内部の内容、パイプの名前、パイプパラメーターの配列、オブジェクトフィールドの名前に関する情報が必要です。
{{__ | _:1:2}}
便宜上、いくつかのインターフェースを定義しましょう。
interface PipedObject { property: string; // __ pipe: PipeDefinition; // } interface PipeDefinition { name: string; // _ params: string[]; // }
解析関数:
private parseTranslation(res: string): {pipedObjects: PipedObject[], matches: string[]} { // "{{dateField | dateFormat:LL | additionalPipe:param1}}", "{{anotherField | customPipe:param1:param2}}" let matches = res.match(/{{.[^{{]*}}/g); let pipedObjects: PipedObject[] = []; (matches || []).forEach((v) => { // {{dateField | dateFormat:LL}} -> dateField|dateFormat:LL|additionalPipe:param1 v = v.replace(/[{}\s]+/g, ''); // : dateField|dateFormat:LL|additionalPipe:param1 let pipes = v.split('|'); let objectPropertyName = pipes[0]; // dateField pipes = pipes.slice(1); // [dateFormat:LL, additionalPipe:param1] for(let pipe of pipes) { // customPipe:param1:param2 -> ['customPipe', 'param1', 'param2'] let pipeTokens = pipe.split(':'); pipedObjects.push({ property: objectPropertyName, pipe: { name: pipeTokens[0], // customPipe params: pipeTokens.slice(1) // ['param1', 'param2'] } }); } }); return {pipedObjects, matches}; }
さて、まずは、parseTranslationと、1つの値のみをコンパイルする関数をバインドします-compile
public compile(value: string, lang: string): string | Function { return this.compileValue(value); } private compileValue(val): Function { let parsedTranslation = this.parseTranslation(val); // , return (argsObj: object) => { // - ngx-translate // : value1: {{dateField | dateFormat:LL}} and value2: {{anotherField | customPipe:param1:param2}} let res = val; parsedTranslation.pipedObjects.forEach((o, i) => { // DI , . const pipe = this.injector.get(o.pipe.name); const property = argsObj[o.property]; // argsObj - - [translateParams] // pipe.transform(prop, o.pipe.params); const pipeParams = this.assignPipeParams(argsObj, o.pipe.params || []); if(!property) { return res; } let pipedValue = pipe.transform( property, pipeParams.length === 1 ? pipeParams[0] : pipeParams ); // {{*.}} . {{dateField | dateFormat:LL}} -> 15:00 res = res.replace(parsedTranslation.matches[i], pipedValue); }); return res; }; } private assignPipeParams(obj: object, params: string[]) { let assignedParams = []; params.forEach(p => { if(obj.hasOwnProperty(p)) { assignedParams.push(obj[p]); } else { assignedParams.push(p); } }); return assignedParams; }
明確にするために、assignPipeParamsを使用すると、コンポーネントからcustomPipeにパラメーターを渡すことができます。
"@SOME": "value1: {{dateField | dateFormat:LL}} and value2: {{anotherField | customPipe:param1}} // :LL :param1 - <div>{{'@SOME' | myTranslate:{dateField: entity.value1, anotherField: entity.value2, param1: 'DYNAMIC_VALUE'} }}</div> // :param1
compileTranslationsからすべてのjsonを解析する方法については説明しません。スタックオーバーフローでは、json構造全体をトラバースするための多くのメソッドを見つけることができます。
public compileTranslations(translations: any, lang: string): any { this.iterateTranslations(translations); return translations; } private iterateTranslations(obj) { for (let key in obj) { if (obj.hasOwnProperty(key)) { const val = obj[key]; const isString = typeof val === 'string'; if(key[0] === '@' && isString) { obj[key] = this.compileValue(val); // compileValue } else if(!isString) { this.iterateTranslations(val); } } } }
できた! ngx-translate json内でパイプを使用できるようになりました!
ここでは、Angular Elementsとは何かを簡単に、本質的に見ることができます。
ビデオからのソースがあります
一般に、Angular Elementsはまだ実験的であるため、AngularからWebコンポーネントをコンパイルしてJavaScriptアプリケーションで使用することはできません。
しかし! しかし、その利点はすでにあります。 これで、Angular 5のセルフブートストラップコンポーネントを作成でき、初期化段階でのみアプリケーション全体のブートストラップを行いましたが、アプリケーションをHTML文字列から実行している間はいつでもコンポーネントを「コンパイル」できます。
これは、特定のHTML文字列をAPIからコンポーネントに変換する必要がある場合に役立ちます。
ただし、ComponentTranslateCompilerの作成にも役立ちます。 ngx-translate json`ovからコンポーネントを収集できます。
以上です。 研究と改善!