Reputation: 20707
I'm building an angular app with ngx-translate. It's working nicely. Now I'm starting to have data coming from my backend(firebase), for which I will have translations.
What is the correct way of displaying localized data from a model? I've not yet settled on the data format, so either it will be something like:
{
'title_fr': 'mon titre',
'title_en': 'my title',
'title_de': 'mein Titel',
'description_fr' : '....'
//...
}
either something like:
{
title: {'fr':'mon titre', 'en': 'my title', 'de': 'mein Titel'}
description: { 'fr': 'ma description', /*...*/}
}
and I'm not sure how to display them:
ngFor
iterating on observable of those items, if I could avoid to having to map every data, it would be greatWhat would you recommend me?
Upvotes: 0
Views: 5225
Reputation: 807
The library @ngx-translate/http-loader allows loading the translations using http.
Bellow is an example of how I use ngx-translate
along with http-loader
in the applications I develop.
I have made a working stackblitz of the code bellow (available here):
Defining the data structure for the i18n strings
In that example, for each language there is a url where the translation data (json
) can be retrieved in the following format:
{
welcome: "Welcome",
subscription_msg: "Subscribe now"
}
Creating one webservice for each supported language
Consider one application that supports 3 languages: en
, pt
and fr
.
I usually have one url for each language. Each url returns a json with key
- value
pairs where the keys are the labels I use in the frontend, and the values are the messages I want to show the users in that language.
In that example, I have create 3 mock urls with the translations for each language as follows:
English - Mock url: https://run.mocky.io/v3/33f736b0-e73a-499e-8a50-01e66041d634
{
welcome: "Welcome",
subscription_msg: "Subscribe now"
}
Portuguese - Mock url: https://run.mocky.io/v3/db1e37da-342e-4918-8ce2-bd30aa12fe79
{
welcome: "Bem-vindo",
subscription_msg: "Inscreva-se gratuitamente"
}
French - Mock url: https://run.mocky.io/v3/6960c960-ea66-42a0-87f1-f34568ecb740
{
welcome: "Bienvenue",
subscription_msg: "Abbonez-vous"
}
To output the subscription_msg
in the frontend, I use the ngx-translate
syntax like that:
{{ 'subscription_msg' | translate}}
Installing @ngx-translate/http-loader
Assuming ngx-translate
is already installed, this is the command for installing http-loader
:
npm install @ngx-translate/http-loader --save
Creating your own TranslationLoader
We need to make an implementaion of TranslationLoader
interface. It has just one method (getLanguage()
) which is responsible for loading the map of i18n messages for the current language.
Bellow is our simple implementation called TranslationHttpLoader
. It is invoked when the language is changed:
export class TranslationHttpLoader implements TranslateLoader {
constructor(private httpClient: HttpClient) {}
public getTranslation(lang: string): Observable<Object> {
if (lang == null) {
lang == "en";
}
let urls = {
en: "https://run.mocky.io/v3/33f736b0-e73a-499e-8a50-01e66041d634",
pt: "https://run.mocky.io/v3/db1e37da-342e-4918-8ce2-bd30aa12fe79",
fr: "https://run.mocky.io/v3/6960c960-ea66-42a0-87f1-f34568ecb740"
};
let observer = new Observable(observer => {
this.httpClient.get(urls[lang]).subscribe(
data => {
observer.next(data);
observer.complete();
}
);
});
return observer;
}
}
Configuring the translation provider
In the app.module.ts
we should define a HttpLoaderFactory
:
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslationHttpLoader(httpClient);
}
and associate it with the provided TranslateLoader
:
@NgModule({
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
Testing - changing language and showing localized messages
To test
<select>
with the languages as options and a (change)
event handler.ngx-translate
syntax {{ 'key_in_json_file' | translate }}
.app.component.html
<div>
<label>Language</label>
<select (change)="changeLanguage($event)">
<option value="en">English</option>
<option value="pt">Português</option>
<option value="fr">Français</option>
</select>
<p>
{{'welcome' | translate}}.
</p>
<p>
{{'subscription_msg' | translate}}
</p>
</div>
When the (change)
event takes place, all we have to do is to call translate.use(lang)
. Our loader will retrieve the data for the selected language in the url provided and will supply it to ngx-translate
:
app.component.ts
export class AppComponent {
defaultLang: string = "en";
constructor(private translateService: TranslateService) {}
ngOnInit() {
this.translateService.use(this.defaultLang);
}
changeLanguage(event) {
let lang = (event.target as HTMLInputElement).value;
this.translateService.use(lang);
}
}
Output preview
Next steps
Some enhacements can be done in the code provided here, for example:
translateService.getBrowserLang()
TranslationHttpLoader
so it can work offline with default tokens for each language (especially useful in mobile applications).The code described above can be seen working here in this stackblitz: https://stackblitz.com/edit/angular-ivy-pwcrvy?file=src/app/translation-http-loader.ts
Upvotes: 2
Reputation: 115
If you are able to define the data you can get from your backend then probably you can make the data structure as below,
{
"dataLocalization": [{
"locale": "fr",
"title": "mon titre",
"description":"ma description"
},
{
"locale": "en",
"title": "my title",
"description":"my description"
}]
}
Then you can find your locale by findIndex method in the array dataLocalization and use its title and description values
Upvotes: 0