Main class to be used by user isI18NService. I18N is the standard acronym for the word Internationalization. It's implemented as a Decorator for other classes that implement particular logic.
I18NServiceshould be included in the project root class, and then it's accessible as a singleton everywhere in the application.
Localization
By default, the service takes browser language and sets default locale accordingly. User can always change locale by usingsetLocale(locale: string)method. This method takes standard locales as a string, likeen-US, fr-FR, fr-CAetc. First part is language, second is country.
/**
* <p> Sets new locale and returns Promise when new dictionary is uploaded to memory.</p>
* <p> Locale format examples: en-US, fr-FR, fr-CA, de-DE etc.</p>
*/
setLocale(locale: string): Promise<any>
Dates Formatting
Use this method to format date with current locale and pass component name if you want to use date format from component dictionary:
/**
* <p> Format date according to the current locale.</p>
* <p> Date format is taken from dictionary.</p>
*/
formatDate(value: any, componentName?: string): string
Service wrapsmoment.jsto provide date formatting and, therefore, can take its acronyms as date formats:
/**
* <p> Format date according to the current locale.</p>
* <p> Date format corresponds to the moment.js tokens:</p>
* <p> LT - Time</p>
* <p> LTS - Time with seconds</p>
* <p> L - Month numeral, day of month, year</p>
* <p> l - Month numeral, day of month, year - shortcut</p>
* <p> LL - Month name, day of month, year</p>
* <p> ll - Month name, day of month, year - shortcut</p>
* <p> LLL - Month name, day of month, year, time</p>
* <p> lll - Month name, day of month, year, time - shortcut</p>
* <p> LLLL - Month name, day of month, day of week, year, time</p>
* <p> llll - Month name, day of month, day of week, year, time - shortcut</p>
*/
formatDateWithTokens(value: any, format: string): string {
return this.dateFormat.formatDateWithTokens(value, format);
}
By convention, date format should be under "dateFormat" path, for example:
{
"dateFormat": "MM/DD/YYYY"
}
Currency Formatting
Service uses standard JavaScript possibilities to format currency. Method to format it is:
/**
* <p> Format currency according to the current locale.</p>
*/
formatCurrency(value: number, componentName?: string): string {
return this.currencyFormat.formatCurrency(value, this.locale);
}
As for other methods, you can pass component name to take currency format from component dictionary. By convention, currency format should be under "currency" path, for example:
Dictionary is an interface to translate messages. These messages are placed in the JSON files named by language, ex: en.json for English language and fr.json for French language. There is a default dictionary placed underassets/i18nfolder. User can create its own default dictionary and set its URL:
/**
* <p> Sets new dictionary URL.</p>
* <p> Dictionary files must be named according to the locales, ex: en.json, fr.json etc.</p>
* <p> Therefore, if the URL is "http://my_site/my_dictionary", the system will try to find
* new dictionaries under "http://my_site/my_dictionary/en.json", "http://my_site/my_dictionary/fr.json" etc.</p>
*/
setDictionaryURL(path: string): Promise<any> {
return this.dictionary.setDictionaryURL(path);
}
Every component can use its own dictionary by setting its URL in:
/**
* <p> Sets new dictionary URL for the particular component.</p>
* <p> Dictionary files must be named according to the locales, ex: en.json, fr.json etc.</p>
* <p> Therefore, if the URL is "http://my_site/my_dictionary", the system will try to find
* new dictionaries under "http://my_site/my_dictionary/en.json", "http://my_site/my_dictionary/fr.json" etc.</p>
* <p> NOTE: If dictionary for this path and locale is already in memory, then it will NOT be reset.</p>
*/
setComponentDictionaryURL(path: string, componentName: string): Promise<any> {
return this.dictionary.setComponentDictionaryURL(path, componentName);
}
User can get translated messages by using this method:
/**
* <p> Looks for the translated value from the current dictionary.</p>
*/
lookup(path: string, componentName?: string): any {
return this.dictionary.lookup(path, this.getLanguageId());
}
When using the particular component dictionary, you should pass component name as a parameter.
Example
The complete example code can be found under Examples/Internationalization of the Cedrus/CodeFusion project.
Important note is that when user changes locale, another dictionary should be put in memory. Therefore, setLocale() method, as well as setDictionaryURL() and setComponentDictionaryURL() methods returns promises to let system wait until new dictionary is downloaded.
This is the component model and simple label internationalization using the default dictionary:
button1: ButtonModel = {
label: "",
color: {
foreground: "black",
background: "accent"
},
icon: null,
icon_position:"right"
};
ngOnInit() {
// get localized messages from current dictionary
this.button1.label = this.i18nService.lookup('customer.form.validate');
}
If user changes locale, for example, by choosing country in drop down list, then in the listener, the messages should be reset from new dictionary:
setLocale(): void {
this.i18nService.setLocale(this.localeName)
.then(() => {
// after resetting locale, we have to reset localized messages from new current dictionary
this.button1.label = this.i18nService.lookup('customer.form.validate');
});
}
Internationalize Business Component
Lets internationalize the part of the CfWeatherComponent. First, we have to import i18n service and a Subscription class:
import { I18NService } from '../../services/i18n-service/i18n.service';
import { Subscription } from 'rxjs/Subscription';
Then, we should create a method where we put all our internationalization code. Note, that if we want to use a separate dictionary for this component, we can do it with setComponentDictionaryURL() method:
/**
* <p> This method internationalizes the component.</p>
*/
internationalize(locale: string): void {
if (this.enabledI18N) { // verify if internationalization is enabled
this.i18nService.setComponentDictionaryURL('/assets/i18n/weather', 'weather')
.then(() => {
this.title = this.i18nService.lookupByComponent('weather.title', 'weather');
this.input.placeholder = this.i18nService.lookupByComponent('weather.placeholder', 'weather');
});
}
}
Then, we should call internationalize() method and, if we want the component to change its messages when user changes locale, we should subscribe on the locale changes:
onLangChangeSubscription: Subscription;
ngOnInit() {
// other init code here...
// internationalize component
this.internationalize( this.i18nService.getLocale() );
// re-internationalize component in case of locale change
this.onLangChangeSubscription = this.i18nService.onLangChange.subscribe(locale => {
this.internationalize(locale);
});
}
Also, do not forget to unsubscribe when component is destroyed to avoid memory leaks:
But they will not react on the fact that user changes the localization dynamically, for example in drop-down list and passes it to i18n service. They will react only on browser settings changes.
But, all methods provided by i18n service will react on dynamic locale changes.