i18n: Able to use translation strings outside a template #11405
Comments
I think this is a real showstopper, i18n is not ready to use until this is implemented. |
@Mattes83 Did you try this way? <p [hidden]="!alertManagerRowForm.controls.sendTo.hasError('validateEmail')" class="help-error">
{{ 'ALERTMANAGER__FORM__FIELD__sendTo__ERROR__validateEmail' | translate }}
</p> |
|
Yeah same here, I've just switched from ng2-translate to Angular2 i18n as I prefer to use OOTB modules, and it's much easier to extract translations (ng2-translate is more time consuming IMO) |
If someone want to start a design spec, it would be helpful (ie on Google Docs). It would need to list all the cases. Quickly thinking about it, I see a need for
|
Hi guys, great feature request Is there any suggested workaround to translate *.ts texts? |
@fbobbio What I've been doing is create hidden elements in the template, ex. Bind them using: Then retrieve translated value Looks retarded, but works for my needs. |
As ng-xi18n already processes the entire TS-code, why not implement a decorator like
and feed the source into the property when there is no translation target. |
This is an essential item in the whole internationalization cycle, IMO. In my shop, we're accustomed to an "ng-xi18n - like" tool (an extension to xgettext) that crawls all types of source files looking for marked text to put into dictionary files for the translators. I love the clean and easy i18n markup in the HTML templates, and I was expecting the same for Typescript code. |
@vicb In addition to your use cases I'm thinking about possibility to support localization of interpolated string in TS code. However it's likely be needed to rewrite TS code to support such a scenario. Will it be a valid approach? |
This is the primary feature stopping us from using the out-of-the-box approach to translation Angular 2 offers. We have many metadata driven components whose keys come from some metadata not stored in HTML. If we could translate via pipe or programmatically against the available TRANSLATIONS, we could get these components to show the proper strings. In the mean time, we are limited to something like ng-translate because of this limitation. |
The way I worked around this problem is by adding an empty 'lang' object in a service used throughout my application. I then apply a directive that reads all span in a div and store the value in that object. I place all my string in a template at the bottom of the page with an hidden property. The string is then reachable from the template or the component. It's ugly and you could easily overwrite an entry with the same id. But it's better then nothing. SERVICE
DIRECTIVE
TEMPLATE
|
Hello @lvlbmeunier Thank you for providing this suggestion for a workaround while we are waiting for the official implementation. I tried to implement your solution but I cannot seem to get the dynamic translation keys to get recognized. I was trying to do it like this:
Those new keys don't show up in my xliff files. Is it possible to achieve this with your solution? |
I never tried it but i'm almost certain that the build of the xliff file does not run code. Having dynamic value in i18n would be contrary to the concept. If you know for certain of all the name that would be in your list they should all be declared independently and not in a for loop. |
Adding the keys manually works but it is impractical. In my case I get hundreds of text keys in need of translation from an API. |
You can run your code and take the source and copy it to a file. The generation of the x18n file is based on static file. |
With
With this we can, at least for the JIT compiler, create a dummy Component (it doesn't need to be added to the app html, just the app module) with all our "extra" translations. For starters we'll add the providers not just to the compiler but to the app module as well: // bootstrap.ts
getTranslationProviders().then(providers => {
const options = { providers };
// here we pass "options.providers" to "platformBrowserDynamic" as extra providers.
// otherwise when we inject the token TRANSLATIONS it will be empty. The second argument of
// "bootstrapModule" will assign the providers to the compiler and not our AppModule
platformBrowserDynamic(<Provider[]>options.providers).bootstrapModule(AppModule, options);
}); Then we'll create a dummy component to house our extra translations, don't forget to add the component to //tmpI18N.ts
import {Component} from '@angular/core';
@Component({
selector: 'tmpI18NComponent',
moduleId: module.id,
templateUrl: 'tmp.i18n.html'
})
export class TmpI18NComponent {
} Add our translations to <!-- tmp.i18n.html -->
<span i18n="test@@mainTitle">
test {{something}}
</span> Now we can create a service where we can fetch our translations: import {Injectable, Inject, TRANSLATIONS} from '@angular/core';
import {I18NHtmlParser, HtmlParser, Xliff} from '@angular/compiler';
@Injectable()
export class I18NService {
private _source: string;
private _translations: {[name: string]: any};
constructor(
@Inject(TRANSLATIONS) source: string
) {
let xliff = new Xliff();
this._source = source;
this._translations = xliff.load(this._source, '');
}
get(key: string, interpolation: any[] = []) {
let parser = new I18NHtmlParser(new HtmlParser(), this._source);
let placeholders = this._getPlaceholders(this._translations[key]);
let parseTree = parser.parse(`<div i18n="@@${key}">content ${this._wrapPlaceholders(placeholders).join(' ')}</div>`, 'someI18NUrl');
return this._interpolate(parseTree.rootNodes[0]['children'][0].value, this._interpolationWithName(placeholders, interpolation));
}
private _getPlaceholders(nodes: any[]): string[] {
return nodes
.filter((node) => node.hasOwnProperty('name'))
.map((node) => `${node.name}`);
}
private _wrapPlaceholders(placeholders: string[]): string[] {
return placeholders
.map((node) => `{{${node}}}`);
}
private _interpolationWithName(placeholders: string[], interpolation: any[]): {[name: string]: any} {
let asObj = {};
placeholders.forEach((name, index) => {
asObj[name] = interpolation[index];
});
return asObj;
}
private _interpolate(pattern: string, interpolation: {[name: string]: any}) {
let compiled = '';
compiled += pattern.replace(/{{(\w+)}}/g, function (match, key) {
if (interpolation[key] && typeof interpolation[key] === 'string') {
match = match.replace(`{{${key}}}`, interpolation[key]);
}
return match;
});
return compiled;
}
} Now we can do something like: export class AppComponent {
constructor(i18nService: I18NService) {
// Here we pass value that should be interpolated in our tmp template as a array and
// not an object. This is due to the fact that interpolation in the translation files (xlf for instance)
// are not named. They will have names such as `<x id="INTERPOLATION"/>
// <x id="INTERPOLATION_1"/>`. And so on.
console.log(i18nService.get('mainTitle', ['magic']));
}
} This is a hacky workaround. But at least we can leverage the translation file, get by key, interpolate and not have to have a hidden html in our application. NOTE: the current |
@fredrikredflag Awesome! And what about AOT? |
@ghidoz AOT is another story. What we would like to do is to precompile all translations so we can get them by key. But since the DON'T DO IT FOR ANY PRODUCTION APPS. /// bootstrap.aot.ts
function fetchLocale() {
const locale = 'sv';
const noProviders: Object[] = [];
const translationFile = `./assets/locale/messages.${locale}.xlf`;
return window['fetch'](translationFile)
.then(resp => resp.text())
.then( (translations: string ) => [
{ provide: TRANSLATIONS, useValue: translations },
{ provide: TRANSLATIONS_FORMAT, useValue: 'xlf' },
{ provide: LOCALE_ID, useValue: locale }
])
.catch(() => noProviders);
}
fetchLocale().then(providers => {
const options = { providers };
platformBrowser(<Provider[]>options.providers).bootstrapModuleFactory(AppModuleNgFactory);
}); |
@lizzymendivil according to https://is-angular-ivy-ready.firebaseapp.com Ivy is 65% complete and it seems unlikely to be completed in only 30 days |
Yes, ivy will not be finished in one month. |
@ocombe can you please lock this down so only you can post updates. its a little annoying to get all the notifications of all the "when is it done?" comments |
@ocombe it seems like Ivy is now at roughly 94%, do you think this may be ready by the end of the year? |
I don't think so. Being feature ready and bug free (= usable) is very different. We're mostly working on fixing things right now. |
@ocombe can we believe that i18n came before angular 8? |
Ok, thanks for the prompt reply, much appreciated. |
Any update @ocombe that by when we should expect ( Year End i see in last few comments ) the "Code Translation" part available along with "Template Translation" for I18N support with Angular? We thought to club ngx-translate-polyfill for the same along with Angular I18n feature but there also it seems xlf support is not available for merging existing xlf file by using https://github.com/biesbjerg/ngx-translate-extract CLI. Thanks ! |
Thanks for the suggestion! It seems the easiest to understand. My question is, do you still use this implementation in situations where the array has a "lot" of a values, like 10 or more? Seems like the code would get very bulky. |
I dont know if we have any with that many, but why not. If it’s messages used several places we make small i18n components that are basically just the switch and i18n tags.
David
… Den 23. jan. 2019 kl. 19.24 skrev Andrew Bissada ***@***.***>:
@Abekonge
We use ngSwitch to template all messages, so we can attach i18n-tags to them individually.
It's somewhat verboose - but it works!
Thanks for the suggestion! It seems the easiest to understand. My question is, do you still use this implementation in situations where the array has a "lot" of a values, like 10 or more? Seems like the code would get very bulky.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
How to user i18n-polifills in Validator classes. I a not able to user covert validator message through i18n service . //In componenet //Validator import { AbstractControl, ValidationErrors, FormGroup, FormControl } from '@angular/forms';
getting below error RROR Error: Uncaught (in promise): TypeError: i18n is not a function |
How to use i18n-polyfills with const in .ts files export const glossayModel= () => [
]]}]; |
{VAR_SELECT, select, Medical {Medical} Dental {Dental} Medical & Dental {Medical & Dental} Catastrophic & Dental {Catastrophic & Dental} Catastrophic {Catastrophic} }
{VAR_SELECT, select, Medical {Medical} Dental {Dental} Medical### & Dental {Medical y Dental}
Catastrophic ### & Dental{Catastrophic y Dental} Catastrophic {Catastrophic} }
When i am using ICU expression with special character , its not converting . |
@ocombe How to handle breadcrumbs using i18n-polyfills? export const routes: Routes = [ |
You can use like this |
@JanneHarju |
Here is my translation script: and here is xliffmerge.json
|
@JanneHarju But const i have in different .ts files export const glossayModel= () => [ i am importing in component , when i m trying to use i18n with const , it is not extracting the values . |
What if you use const values like provider.
code might be broken but you maybe get point. |
how to translate breadcrumbs const routes: Routes = [ |
can you people stop using this thread as a support thread for i18n-polyfill or other issues? if you have a question regarding i18n-polyfill go raise an issue in that repo. |
Yes, locked for now. |
For what it is worth the new E.g.
And then will be able to provide translations for |
This is now available (if not documented). |
I'm submitting a ... (check one with "x")
Current behavior
#9104 (comment)
Expected/desired behavior
Be able to translate strings used anywhere in the code, using an API.
Reproduction of the problem
What is the expected behavior?
I'm referencing the usage of
$translate.instant
to exposure real use cases:More examples:
Please tell us about your environment:
@vicb
The text was updated successfully, but these errors were encountered: