¿angular-i18n para soluciones en código?
angular5 (3)
Tenemos que esperar hasta Angular 6 para angular-i18n para admitir traducciones en código para mensajes de error y tal.
Para aquellos que usan angular-i18n (en lugar de ngx-translate, por ejemplo), ¿qué están haciendo mientras tanto para manejar las traducciones en código? Se me ocurre que si no hay muchas cadenas, entonces un servicio de lenguaje simple con métodos para obtener traducciones por código de idioma y una identificación funcionaría, pero estoy interesado en algo más elegante y "angular".
No sé cómo será el soporte de las traducciones de código prometidas, pero cualquier solución temporal idealmente se convertiría fácilmente a la forma angular-i18n cuando se active.
¿Qué están haciendo las personas para manejar este problema? ¿Algunas ideas?
Este polyfill parece ser la mejor manera de hacerlo en este momento:
https://github.com/ngx-translate/i18n-polyfill
Le permite ajustar todo lo que desee traducir en una función
i18n()
(es probable que esta API se conserve en una versión futura de Angular; consulte mis notas al final de esta respuesta).
El polyfill está escrito principalmente por Olivier Combe, miembro del equipo de Angular responsable de i18n:
Para Angular 5, necesitará la versión 0.2.0 cuando instale:
npm install @ngx-translate/[email protected] --save
Para Angular 6, obtenga la última versión, actualmente 1.0.0:
npm install @ngx-translate/[email protected] --save
Obtuve el polyfill trabajando para la compilación JIT y AOT , para Angular 5 (también funcionará para Angular 6). Esto es lo que necesita hacer para traducir a un solo idioma (esta es una buena manera de hacer que esto funcione; luego puede hacer que varios idiomas funcionen más adelante, lo que explico más adelante):
app.module.ts
Agregue las siguientes importaciones a su módulo angular de raíz:
import { TRANSLATIONS, TRANSLATIONS_FORMAT } from ''@angular/core'';
import { I18n } from ''@ngx-translate/i18n-polyfill'';
agregue la siguiente constante y especifique los proveedores en su módulo raíz:
// add this after import + export statements
// you need to specify the location for your translations file
// this is the translations file that will be used for translations in .ts files
const translations = require(`raw-loader!../locale/messages.fr.xlf`);
@NgModule({ ....
providers:
[
I18n,
{provide: TRANSLATIONS, useValue: translations},
{provide: TRANSLATIONS_FORMAT, useValue: ''xlf''},
...
Nota sobre el uso de la compilación AOT : si está utilizando la compilación AOT para traducir sus plantillas, la traducción de los mensajes en archivos .ts aún se realizará en tiempo de ejecución usando la compilación JIT (es por eso que necesita hacer referencia a
TRANSLATIONS
yTRANSLATIONS_FORMAT
lugar de solo especificar estos en sus scripts de compilación).
* .ts
En el archivo .ts donde desea proporcionar una traducción, agregue esto:
import { I18n } from ''@ngx-translate/i18n-polyfill'';
constructor(private i18n: I18n) {
console.log(i18n("This is a test {{myVar}} !", {myVar: "^_^"}));
}
Esto demuestra que incluso puede incluir interpolaciones en los mensajes que desea traducir.
Puede usar definiciones i18n (es decir, especificar la traducción ''fuente'' id, significado, descripción) de esta manera:
this.i18n({value: ''Some message'', id: ''Some message id'', meaning: ''Meaning of some message'', description: ''Description of some message''})
Aún necesitará extraer los mensajes, y puede usar la herramienta ngx-extractor para hacerlo. Esto se incluye cuando instala el polyfill, y he agregado un ejemplo a continuación sobre su uso dentro de un script npm. Vea también el archivo Léame en la página de polyfill .
Múltiples idiomas
Para admitir el cambio entre varios idiomas, necesitará un proveedor de fábrica para sus traducciones.
Hay detalles sobre el archivo Léame de la
https://github.com/ngx-translate/i18n-polyfill
.
Necesitará algo como esto en su módulo raíz (o para la compilación AOT, reemplace el valor de retorno de
localeFactory
con una función que detecte qué variante de idioma compilado AOT de su aplicación se está ejecutando actualmente):
export function localeFactory(): string {
return (window.clientInformation && window.clientInformation.language) || window.navigator.language;
}
providers:
[
{
provide: TRANSLATIONS,
useFactory: (locale) => {
locale = locale || ''en''; // default to english if no locale provided
return require(`raw-loader!../locale/messages.${locale}.xlf`);
},
deps: [LOCALE_ID]
},
{
provide: LOCALE_ID,
useFactory: localeFactory
},
Extracción de mensajes y xliffmerge
Todo esto es compatible con xliffmerge , que es una gran herramienta para combinar automáticamente cualquier traducción nueva que agregue, sin sobrescribir las traducciones existentes. Xliffmerge también puede realizar traducciones automáticamente usando el traductor de Google (necesitará una clave API de traductor de Google). Para que esto funcione, hago la extracción y la fusión / traducción en el siguiente orden, antes de hacer la compilación AOT real:
"extract-i18n-template-messages": "ng xi18n --outputPath=src/locale --i18n-format=xlf",
"extract-i18n-ts-messages": "ngx-extractor --input=/"src/**/*.ts/" --format=xlf --out-file=src/locale/messages.xlf",
"generate-new-translations": "xliffmerge --profile xliffmerge.json en fr es de zh"
La compilación AOT para una versión de idioma específica del sitio se ve así:
"build:fr": "ng build --aot --output-path=dist/fr --base-href /fr/ --i18nFile=src/locale/messages.fr.xlf --i18nFormat=xlf --locale=fr",
Estado actual de este polyfill:
Esto está escrito principalmente por Olivier Combe, miembro del equipo de Angular responsable de i18n. En esta etapa, se trata de un polyfill ''especulativo'' para traducir variables o cadenas en el archivo .ts. Es probable que sea reemplazado por una API integrada en Angular que será muy similar, por lo que la actualización posterior debería ser razonablemente manejable. Aquí está el diclaimer de la página de Github:
Esta biblioteca es un polyfill especulativo, significa que se supone que debe reemplazar una API que vendrá en el futuro. Si la API es diferente, se proporcionará una herramienta de migración si es posible y necesario.
Se ha debatido sobre el soporte en las próximas versiones menores de Angular 6 para las traducciones de variables / cadenas en código.
Aquí hay una cita de Olivier Combe (de marzo de este año), de la siguiente discusión sobre Github:
https://github.com/angular/angular/issues/11405
El primer RP para el tiempo de ejecución i18n se ha fusionado en maestro, junto con una aplicación de demostración de hello world que usaremos para probar las características. Funciona en tiempo de ejecución y admite teóricamente traducciones de código, incluso si aún no hay servicio para ello. Por ahora es un soporte mínimo (cadenas estáticas), estamos trabajando para agregar nuevas características (haré que la extracción funcione la próxima semana, y luego cadenas dinámicas con marcadores de posición y variables). Después de eso, haremos el servicio de traducción de códigos. Tan pronto como finalice una nueva función, se fusionará con la maestra, no tendrá que esperar a una nueva especialización.
Ha pasado mucho tiempo desde mi última respuesta y se puede simplificar.
si nuestro .html es como
<!--at first we put all the translation in a hidden div-->
<div *ngIf="!yet" style="display: none">
<span #t id="message1">Translation app</span>
<span #t id="message2">Hola Mundo</span>
</div>
Tenemos en nuestro .ts un ViewChildren simple, una variable "todavía" y una variable "traducción"
yet: boolean = false;
translation:any={}
@ViewChildren("t")
set translations(values: QueryList<any>) {
//when yet becomes true, values.length=0
if (values.length) {
values.forEach(c => {
this.translation[c.nativeElement.id]=c.nativeElement.innerHTML
})
//it''s necesary enclosed this.yet=true in a setTime to avoid
//the error expresion changes After Exec
setTimeout(()=>{
this.yet=true;
})
}
}
entonces podemos escribir algunos como
alert(this.translation[''message1'']);
Actualización La misma idea: una implementación de componente tiene un componente
import { Component, QueryList, AfterViewInit, ContentChildren } from ''@angular/core'';
@Component({
selector: ''ng-translation'',
template: `
<div *ngIf="!yet" [style.display]="''none''">
<ng-content></ng-content>
</div>
`
})
export class TranslationComponent implements AfterViewInit {
@ContentChildren("t") translations: QueryList<any>
data: any = {}
yet: boolean = false;
get(text: string) {
return this.data[text];
}
ngAfterViewInit(): void {
if (this.translations.length) {
this.translations.forEach(c => {
this.data[c.nativeElement.id] = c.nativeElement.innerHTML
})
setTimeout(() => {
this.yet = true;
})
}
}
}
En cualquier otro componente
<ng-translation #translation>
<span #t id="message1">Translation app</span>
<span #t id="message2">Hola Mundo</span>
</ng-translation>
@ViewChild("translation") translation:TranslationComponent
click()
{
alert(this.translation.get(''message1''));
}
Tengo una solución "extraña" Podemos tener dos componentes
app-text.component.ts
import { Component} from ''@angular/core'';
@Component({
selector: ''text'',
template:`<ng-content></ng-content>`
})
export class AppTextComponent{}
y app-translation.component.ts
import { Component, QueryList, ElementRef, ContentChildren } from ''@angular/core'';
import { AppTextComponent } from ''./app-text.component'';
@Component({
selector: ''app-translation'',
template: `<ng-content></ng-content>`
})
export class AppTranslationComponent{
@ContentChildren(AppTextComponent, { read: ElementRef }) divs: QueryList<ElementRef>;
constructor() { }
translate(id: string): string {
let nativeElement: any = this.divs.find(e => e.nativeElement.id == id);
return nativeElement ? nativeElement.nativeElement.innerText : "";
}
}
Luego, en un componente podemos tener algunos
<app-translation #translations style="visibility:collapsed">
<text id="message1">Translation app</text>
<text id="message2">Hola Mundo</text>
</app-translation>
//In your code you can use a ViewChild and the function "traslate"
@ViewChild(''translations'') t;
alert(this.t.translate("message1"));