Simplest translations for Angular (tested for v10+).
- see plain JS library changes.
- deprecated
init
method in root import. Instead useaddMiddleware
,loadDictionaries
andfinal
methods instead. lang
andfallbackLang
now in the root import.- added middleware pipeline (see Pipeline).
- removed
$less
property. Instead of$less
useplaceholder = 'single'
. - added
fallback
property to directive. defaultLang
renamed tolang
.extend
inforChild
initialization changed toloadDictionaries
.- added language detection change for directives and pipes
- after initialization
lang
andfallbackLang
can be changed only fromTranslateRootService
. - removed dynamic cache.
Please use link above to learn more about basic interaction, dictionaries, pluralization, cases, etc.
npm i simply-translate-angular
import { TranslateModule, TranslateService } from 'simply-translate-angular';
@NgModule({
declarations: [AppComponent, AppViewComponent],
imports: [
TranslateModule.forRoot({
// dependencies
deps: [ HttpClient ],
// language
lang: window.navigator.language,
fallbackLang: 'ru-RU',
// static dictionaries
dictionaries:{'ru-RU':{...}}
// load dictionaries
loadDictionaries:({lang, fallbackLang}, client /* params are injected dependencies received in the same order as they are in deps */) =>{
return {
[lang]: {...}
}
}
})
]
});
<!-- use default language -->
<h2 translate="hello_user" [values]="{ user: 'Oleg' }"></h2>
<!-- use other language -->
<h2 translate="hello_user" to="ru-RU" [values]="{ user: 'Oleg' }"></h2>
<!-- use fallback -->
<h2 translate="hello_user_not_there" [values]="{ user: 'Oleg' }">Hello user</h2>
<!-- please note that Angular uses curly-braces in templates as well, so prefer use fallback property or replace open bracket with ${ (and optionally closing bracket with }) -->
<h2 translate="hello_user_not_there" [values]="{ user: 'Oleg' }">Hello ${user}</h2>
<!-- preferred fallback property usage -->
<h2 translate="hello_user_not_there" [values]="{ user: 'Oleg' }" fallback="Hello ${user}"></h2>
Directives can detect dynamic language change, but it is not by default. Use [detect] property to detect root service language change.
<h2 translate="hello_user" to="ru-RU" [values]="{ user: 'Oleg' }" detect></h2>
Directive can use inner text as a fallback.
<h2>{{ 'hello_user' | translate: { user: 'Oleg' } }}</h2>
<!-- use other language -->
<h2>{{ 'hello_user' | translateTo: 'ru-RU': { user: 'Oleg' } }}</h2>
<!-- use fallback -->
<h2>{{ 'hello_user_not_there' | translate: { user: 'Oleg' } : 'Hello ${user}'}}</h2>
Pipes are pure by default. However if application has dynamic language change you may use special impure directive (it has internal dirty check), it will detect language changes as well as pipe parameters.
<h2>{{ 'hello_user' | translate$: { user: 'Oleg' } }}</h2>
@Component({
...
})
export class Component {
hello: string;
constructor(private translate: TranslateService) {
// use default language
this.hello = translate.translate('hello_user', { user: 'Oleg' })
// use other language
this.hello = translate.translateTo('ru-RU','hello_user', { user: 'Oleg' })
// use fallback
this.hello = translate.translateTo('ru-RU','hello_user_not_there', { user: 'Oleg' }, 'Hello ${user}')
}
}
To change language use TranslateRootService
lang
property.
To detect changes subscribe on languageChange$
and dictionaryChange$
. Note that loadDictionaries
method in root settings will not execute when language changes.
export class Component {
constructor(private rootTranslate: TranslateRootService){
rootTranslate.lang = "en-US";
}
}
Default forRoot
initialization allows to use injected dependencies (e.g. HttpClient
) to fetch dictionaries. It returns Observable
that contains set of dictionaries
export function getDictionary(lang: string, client: HttpClient) {
return client.get<Dictionary>(`/assets/translations/${lang}.json`);
}
@NgModule({
declarations: [...],
imports: [
...
TranslateModule.forRoot({
// dependencies
deps: [ HttpClient ],
lang: window.navigator.language,
fallbackLang: 'ru-RU',
loadDictionaries: ({lang, fallbackLang}, client /* params are injected dependencies received in the same order as they are in deps */) => {
const res$ = forkJoin([getDictionary(lang, client), getDictionary(fbLang, client)]).pipe(
map((res) => {
return { [lang]: res[0], fbLang: res[1] };
})
);
return res$;
},
}),
...
],
...
})
Note: it is might be useful to hardcode fallback dictionary in .ts or .json file then import it rather then use http client to download.
import fallback from './translations/fallback';
TranslateModule.forRoot({
lang: window.navigator.language,
fallbackLang: env.fallbackLanguage,
dictionaries: {
[env.fallbackLanguage]: fallback,
},
deps: [ HttpClient ],
loadDictionaries: ({lang, fallbackLang}, client /* params are injected dependencies received in the same order as they are in deps */) => {
return getDictionary(lang, client).pipe(
map((res) => {
return { [lang]: res };
})
);
},
}),
Note: For more complex scenarios you may use initialization functions with APP_INITIALIZER
token.
Load dictionaries for Lazy modules a bit trickier.
export function getDictionary(lang: string, client: HttpClient) {
return client.get<Dictionary>(`/assets/translations/${lang}.dynamic.json`);
}
@NgModule({
declarations: [...],
imports: [
TranslateModule.forChild({
deps: [ HttpClient ],
loadDictionaries: ({ lang, fallbackLang }, client: HttpClient) => {
return forkJoin([getDictionary(lang, client), getDictionary(fallbackLang, client)]).pipe(
map((res) => {
return { [lang]: res[0], [fallbackLang]: res[1] };
})
);
},
}),
...
]
})
export class DynamicModule {}
Then you must use resolver TranslateResolve
to every lazy component to wait for child loadDictionaries
.
const routes: Routes = [{ path: '', component: DynamicComponent, resolve: { translate: TranslateResolve } }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class DynamicRoutingModule {}
For rare cases you may use id
parameter for Lazy loaded module, that allows having different values with same key.
"Lazy" values will be available only for lazy modules with that special id
.
@NgModule({
declarations: [...],
imports: [
TranslateModule.forRoot({
dictionaries: {'en-US':{
'key':'Value'
}}
})
]
})
@NgModule({
declarations: [...],
imports: [
TranslateModule.forChild({
id: 'lazy'
dictionaries: {'en-US':{
'key':'Value for Lazy'
}}
})
]
})
(experimental)
Currently it is possible to append middleware to the end of translation pipeline.
It might be specially useful to add logger or rarely fine-tune translation result.
@NgModule({
declarations: [...],
imports: [
...
TranslateModule.forRoot({
// dependencies
deps: [ Logger ],
addMiddleware: (logger /* injected dependencies */) => {
return [
({params, result}, next) => {
if (result.fallingBack) {
logger.log(`Translation absent [${params.lang}:${params.key}]`);
}
next();
},
];
},
}),
...
],
...
})