Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upi18n: Able to use translation strings outside a template #11405
Comments
vicb
added
type: feature
comp: i18n
labels
Sep 7, 2016
This comment has been minimized.
This comment has been minimized.
Mattes83
commented
Sep 20, 2016
|
I think this is a real showstopper, i18n is not ready to use until this is implemented. |
This comment has been minimized.
This comment has been minimized.
marcalj
commented
Sep 20, 2016
•
|
@Mattes83 Did you try this way? <p [hidden]="!alertManagerRowForm.controls.sendTo.hasError('validateEmail')" class="help-error">
{{ 'ALERTMANAGER__FORM__FIELD__sendTo__ERROR__validateEmail' | translate }}
</p> |
This comment has been minimized.
This comment has been minimized.
manklu
commented
Sep 20, 2016
|
|
This comment has been minimized.
This comment has been minimized.
Mattes83
commented
Sep 21, 2016
vicb
referenced this issue
Sep 30, 2016
Merged
docs(i18n): add internationalization (i18n) guide #2491
This comment has been minimized.
This comment has been minimized.
rolandoldengarm
commented
Sep 30, 2016
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) |
This comment has been minimized.
This comment has been minimized.
|
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
|
vicb
added
P1: urgent
and removed
P1: urgent
labels
Sep 30, 2016
choeller
referenced this issue
Sep 30, 2016
Closed
i18n: support variables as translation key #12008
IgorMinar
removed
the
P2: required
label
Oct 4, 2016
vicb
added
the
freq3: high
label
Oct 7, 2016
This comment has been minimized.
This comment has been minimized.
fbobbio
commented
Oct 14, 2016
|
Hi guys, great feature request Is there any suggested workaround to translate *.ts texts? |
This comment has been minimized.
This comment has been minimized.
mstawick
commented
Oct 17, 2016
•
|
@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. |
This comment has been minimized.
This comment has been minimized.
db6edr
commented
Oct 20, 2016
•
|
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 comment has been minimized.
This comment has been minimized.
pharapiak
commented
Oct 20, 2016
•
|
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. |
This comment has been minimized.
This comment has been minimized.
quanterion
commented
Nov 3, 2016
|
@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 comment has been minimized.
This comment has been minimized.
aaronleesmith
commented
Nov 4, 2016
|
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. |
This comment has been minimized.
This comment has been minimized.
lvlbmeunier
commented
Nov 7, 2016
•
|
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
|
This comment has been minimized.
This comment has been minimized.
sknoth
commented
Dec 6, 2016
|
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? |
This comment has been minimized.
This comment has been minimized.
lvlbmeunier
commented
Dec 6, 2016
|
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. |
This comment has been minimized.
This comment has been minimized.
sknoth
commented
Dec 6, 2016
|
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. |
This comment has been minimized.
This comment has been minimized.
lvlbmeunier
commented
Dec 6, 2016
|
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. |
This comment has been minimized.
This comment has been minimized.
fredrikredflag
commented
Dec 20, 2016
•
|
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 |
This comment has been minimized.
This comment has been minimized.
ghidoz
commented
Dec 20, 2016
|
@fredrikredflag Awesome! And what about AOT? |
This comment has been minimized.
This comment has been minimized.
fredrikredflag
commented
Dec 20, 2016
|
@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);
}); |
This comment has been minimized.
This comment has been minimized.
nechamam
commented
Jun 12, 2018
|
@ocombe any news in angular 6? |
This comment has been minimized.
This comment has been minimized.
|
It will not be in Angular v6, as explained above. We depend on Ivy which is planned for v7. |
This comment has been minimized.
This comment has been minimized.
|
@ocombe may I suggest that you lock this thread, so that only the team can give meaningful updates when appropriate and we avoid the repetitive questions that you keep answering ? |
This comment has been minimized.
This comment has been minimized.
petersalomonsen
commented
Jun 13, 2018
|
My hack for use with the current Angular is using an
and then in in the component ts file:
The |
HarelM
referenced this issue
Jun 16, 2018
Closed
i18n: Support changing language inside the app #24549
This comment has been minimized.
This comment has been minimized.
vupham-zingbox
commented
Jul 26, 2018
|
Hopefully the api will work for the routes as well ! For example
|
This comment has been minimized.
This comment has been minimized.
tanekim77
commented
Jul 29, 2018
•
|
If this feature is released, would there be a significant change from the current i18n api? Would you recommend I start my project with i18n now or should I wait for the new i18n Api when Angular 7 gets released? |
This comment has been minimized.
This comment has been minimized.
shobhit12345
commented
Jul 30, 2018
|
Any update When we will get run-time and dynamic translation support? |
This comment has been minimized.
This comment has been minimized.
|
Runtime translations are done and merged (with ivy), but the compiler side is not finished yet. |
This comment has been minimized.
This comment has been minimized.
shobhit12345
commented
Aug 14, 2018
|
@vincentjames501 Can we use i18n-polyfills with ICU expression?i have get no solution how to implement that |
This comment has been minimized.
This comment has been minimized.
djindjic
commented
Aug 14, 2018
|
@ocombe, can you please clarify the difference between runtime vs dynamic translations? |
This comment has been minimized.
This comment has been minimized.
|
there's probably no official denomination, but the way I see it is:
|
riccardo-forina
referenced this issue
Aug 14, 2018
Closed
[NOMERGE] RFC - Switch to Angular's own i18n tools #3381
This comment has been minimized.
This comment has been minimized.
andrei-tatar
commented
Aug 23, 2018
|
I postponed adding i18n support to the current project we're developing but we're getting closer to the release, any idea if this will be final in angular 7? Or should I consider other ways of adding translations? |
This comment has been minimized.
This comment has been minimized.
MickL
commented
Aug 23, 2018
•
|
@andrei-tatar i18n is working perfectly since Angular 2. The only downsides:
For the last two points you could use @ngx-translate instead. But it works differently from Angular's built in i18n so an later update could take some time. For the polyfill the update will be no time with probably no breaking changes. |
This comment has been minimized.
This comment has been minimized.
shobhit12345
commented
Aug 24, 2018
•
|
How we can handle conditional operator using Angular i18n feature *<div class="show-history d-none d-md-block" ngIf="isOk"> |
This comment has been minimized.
This comment has been minimized.
Abekonge
commented
Aug 24, 2018
•
|
We use ngSwitch to template all messages, so we can attach i18n-tags to them individually.
We use this method a lot to get around the missing dynamic translations. Given a bit of thought a surprising amount of text can be expressed in this way in templates. A common pattern is to have an array of messages, and then use this with ngFor and ngSwitch to template the messages, something like this: ts
html
It's somewhat verboose - but it works! |
This comment has been minimized.
This comment has been minimized.
shobhit12345
commented
Aug 24, 2018
|
Thanks a lot , I got the idea .
…On Fri, 24 Aug 2018, 16:56 David Walther Birk Lauridsen, < ***@***.***> wrote:
@shobhit12345 <https://github.com/shobhit12345>
We use ngSwitch to template all messages, so we can attach i18n-tags to
them individually.
<div class="show-history d-none d-md-block" ngIf="isOk">
<ng-container [ngSwitch]="paymentMethod.historyOpned">
<ng-container *ngSwitchCase="true" i18n="@@hide">Hide</ng-container>
<ng-container *ngSwitchCase="false"i18n="@@show">Show</ng-container>
<ng-container>
<ng-container> <ng-container>
<ng-container i18n="@@history">History</ng-container>
</div>
We use this method a lot to get around the missing dynamic translations.
Ggiven a bit of thought a surprising amount of text can be expressed in
this way in templates. A common pattern is to have an array of messages,
and then use this with ngFor and ngSwitch to template the messages,
something like this:
ts
messsages = [ 'this is the first message', 'this is the second message' ]
html
<ng-container *ngFor="let m of messages">
<ng-container [ngSwitch]="m">
<ng-container *ngSwitchCase="this is the first message"
i18n="@@firstMesssage">This is the first message
<ng-container *ngSwitchCase="this is the second message"
i18n="@@secondMesssage">This is the second message
It's somewhat verboose - but it works!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#11405 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGwM6jcWIpwtGxhkH1fwnVXNagRxmMnoks5uT-LxgaJpZM4J2pkr>
.
|
This comment has been minimized.
This comment has been minimized.
lizzymendivil
commented
Sep 30, 2018
It's alraedy October. Do you think we'll have the new features by the end of October? |
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
|
Yes, ivy will not be finished in one month. |
This comment has been minimized.
This comment has been minimized.
JanEggers
commented
Oct 1, 2018
|
@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 |
This comment has been minimized.
This comment has been minimized.
asultan001
commented
Dec 5, 2018
|
@ocombe it seems like Ivy is now at roughly 94%, do you think this may be ready by the end of the year? |
This comment has been minimized.
This comment has been minimized.
|
I don't think so. Being feature ready and bug free (= usable) is very different. We're mostly working on fixing things right now. |
This comment has been minimized.
This comment has been minimized.
NiZelooer
commented
Dec 5, 2018
|
@ocombe can we believe that i18n came before angular 8? |
This comment has been minimized.
This comment has been minimized.
asultan001
commented
Dec 5, 2018
Ok, thanks for the prompt reply, much appreciated. |
This comment has been minimized.
This comment has been minimized.
animbalk
commented
Jan 2, 2019
|
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 ! |
animbalk
referenced this issue
Jan 2, 2019
Open
ngx-translate-extract with xlf support for extracted file. Only JSON available right now. #119
This comment has been minimized.
This comment has been minimized.
andrewbissada
commented
Jan 23, 2019
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. |
This comment has been minimized.
This comment has been minimized.
Abekonge
commented
Jan 23, 2019
|
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.
|
marcalj commentedSep 7, 2016
•
edited by vicb
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.instantto exposure real use cases:More examples:
Please tell us about your environment:
@vicb