Intl Module
Overview
This module provides internationalization (i18n) support to an application. Core functionality is provided by the IntlService
which manages locales and formats dates, numbers and strings depending on the current locale. It is based on the standard
Intl API and an implementation of the
ICU Message syntax provided by FormatJS.
Registering locales
A Sinequa locale defines the name
and display
and the data
associated with the locale. At a minimum the data
will define
the locale for the Intl
library and the messages in the locale's language. Locale data for additional 3rd party libaries
(eg D3.js, Moment.js) can also be included. The supported locales for an application are defined by passing a class derived from
LocalesConfig
to the IntlModule.forRoot
static method when importing the IntlNodule
in your application's NgModule
:
import { LocalesConfig, Locale } from "@sinequa/core/intl";
import enLocale from "../locales/en";
...
export class AppLocalesConfig implements LocalesConfig {
defaultLocale: Locale;
locales?: Locale[];
constructor() {
this.locales = [
{ name: "en", display: "msg#locale.en", data: enLocale},
{ name: "fr", display: "msg#locale.fr", data: frLocale},
{ name: "de", display: "msg#locale.de", data: deLocale},
]
this.defaultLocale = this.locales[0];
}
}
...
@NgModule({
imports: [
...
IntlModule.forRoot(AppLocalesConfig),
]
})
Alternatively, the Core
library includes a default (en-US) locale that can be used to quickly get started:
import { DefaultLocalesConfig } from '@sinequa/core';
...
@NgModule({
imports: [
...
IntlModule.forRoot(DefaultLocalesConfig),
]
})
Locale files
The data for a locale is typically defined in its own file and follows the following structure:
./locales/de.ts
import { LocaleData } from '@sinequa/core/intl';
import 'intl/locale-data/jsonp/de-DE'; // Safari
import '@formatjs/intl-relativetimeformat/dist/locale-data/de'; // relative time format support
import 'moment/locale/de'; // Moment.js
import d3Format from 'd3-format/locale/en-US.json'; // D3.js
import d3Time from 'd3-time-format/locale/en-US.json'; // D3.js
import { enCore } from '@sinequa/core'; // Core language files
// Load language files for those components used...
import { deAdvanced } from '@sinequa/components/advanced';
...
import appMessages from './messages/en.json';
import { Utils } from '@sinequa/core/base';
// Merge the messages
const messages = Utils.merge({}, deCore, deAdvanced, ..., appMessages);
// Export the LocaleData
export default {
intl: {
locale: 'de-DE'
},
moment: {
locale: 'de'
},
d3: {
locale: 'de-DE',
format: d3Format,
time: d3Time
},
messages: messages
} as LocaleData;
Current locale
The current locale is initialized at application startup. It is set in this order:
- the previously selected locale retrieved from local storage (key = 'sinequa-locale')
- a locale matching the browser language
- the deafault locale as specified in
LocalesConfig
The IntlService
can change the current locale to one of the values in the
configured LocalesConfig
. By default, the passed value will be persistent (stored in
local storage).
this.intlService.use('fr');
Each time the locale is changed an event is raised which can be subscribed to like this:
this.intlService.events.subscribe(
(event) => {
console.log('new locale selected:', event.locale);
}
);
Messages
Messages defined in locale message files can be accessed by IntlService.formatMessage
.
The keys must be prefixed by msg#
and the remainder of the key is then a simple JSON path
lookup into the messages object for the current locale. So, for a messages object with the
following structure:
{
"sectionOne": {
"greeting1": "This is greeting one",
"greeting2": "This is greeting two"
},
"sectionTwo": {
"greeting1": "This is greeting one in sectionTwo"
}
}
A call to this.intlService.formatMessage('msg#sectionOne.greeting2')
would return This is greeting two
.
Messages use ICU Message syntax that supports displaying variable values (strings, numbers and dates) and handles
pluralization rules according to the current locale. A map of values can be passed as the second
parameter to IntlService.formatMessage
. For example, if message2
above were changed to Hello {name}
and then
formatted by calling this.intlService.formatMessage('msg#sectionOne.greeting2', {name: 'Tom'});
the result would
be Hello Tom
.
For more information on the ICU Message syntax please follow this link.
The sqMessage
pipe is provided to facilitate the formatting of messages in component templates. Internally, it calls
IntlService.formatMessage
but also handles changes to the current locale, allowing any text displayed through the
pipe to be refreshed automatically. The above example could be rendered in a template as follows:
<span>{% raw %}{{'#msgsectionOne.greeting2' | sqMessage:{values: {name: 'Tom'} } }}{% endraw %}</span>
Note the use of spaces between the closing curly brackets in the parameter passed to the pipe. Without these spaces they would be interpreted as the termination of the interpolated value by the Angular template parser and an error would occur.