Advanced formatting: CLDR

    Note: Internationalized applications that require simple, unformatted locale-specific messages do not need to concern themselves with loading CLDR data. These applications only need to be configured as per an internationalized Dojo application.

    CLDR data can be loaded from an application’s .dojorc build configuration file via the cldrPaths list within the build-app section.

    • cldrPaths: string[]
      • An array of paths to files to load. Can be used in conjunction with the locale and supportedLocales options - if a path contains the string {locale}, that file will be loaded for each locale listed in the locale and supportedLocales properties.

    For example, with the following configuration, the numbers.json CLDR file will be loaded for all three supported en, es, and fr locales:

    Outside of the Dojo build system, CLDR data can be loaded via the loadCldrData method exported by @dojo/framework/i18n/cldr/load. loadCldrData accepts an object of CLDR data. All CLDR data must match the format used by the files. Supplemental data must be nested within a top-level supplemental object, and locale-specific data must be nested under locale objects within a top-level main object.

    For example:

    1. import loadCldrData from '@dojo/framework/i18n/cldr/load';
    2. loadCldrData({
    3. "supplemental": {
    4. "likelySubtags": { ... }
    5. },
    6. "main": {
    7. "en": {
    8. "numbers": { ... }
    9. }
    10. }
    11. });

    Dojo’s i18n module requires the following CLDR data for each particular formatting feature:

    For ICU message formatting:

    • supplemental/likelySubtags
    • supplemental/plurals

    For :

    • main/{locale}/ca-gregorian
    • main/{locale}/dateFields
    • main/{locale}/numbers
    • main/{locale}/timeZoneNames
    • supplemental/likelySubtags
    • supplemental/numberingSystems
    • supplemental/ordinals
    • supplemental/plurals
    • supplemental/timeData
    • supplemental/weekData

    For number/currency formatting:

    • main/{locale}/currencies
    • main/{locale}/numbers
    • supplemental/currencyData
    • supplemental/likelySubtags
    • supplemental/numberingSystems
    • supplemental/ordinals
    • supplemental/plurals

    For :

    • main/{locale}/numbers
    • main/{locale}/units
    • supplemental/likelySubtags
    • supplemental/numberingSystems
    • supplemental/ordinals
    • supplemental/plurals

    The message formatting examples in the next two subsections will use a message bundle with a guestInfo message as follows:

    1. export default {
    2. messages: {
    3. guestInfo: '{host} invites {guest} to the party.'
    4. }
    5. };

    With basic token replacement, an object with host and guest properties can be provided to a formatter without the need to load CLDR data.

    Replacing tokens in widgets

    I18n-aware widgets can use the format function returned from to perform simple token replacement in their messages.

    The guestInfo message can be rendered directly via format:

    Direct token replacement formatting

    The i18n module exposes two methods that handle message formatting:

    • formatMessage, which directly returns a formatted message based on its inputs
    • getMessageFormatter, which returns a method dedicated to formatting a single message.

    Both of these methods operate on bundle objects, which must first be registered with the i18n ecosystem by passing them to .

    1. import i18n, { formatMessage, getMessageFormatter } from '@dojo/framework/i18n/i18n';
    2. import bundle from 'nls/main';
    3. i18n(bundle, 'en').then(() => {
    4. const formatter = getMessageFormatter(bundle, 'guestInfo', 'en');
    5. guest: 'Laura Nader'
    6. });
    7. console.log(message); // "Margaret Mead invites Laura Nader to the party."
    8. // Note that `formatMessage` is essentially a convenience wrapper around `getMessageFormatter`.
    9. message = formatMessage(
    10. bundle,
    11. 'guestInfo',
    12. {
    13. host: 'Marshall Sahlins',
    14. gender: 'male',
    15. guest: 'Bronisław Malinowski'
    16. },
    17. 'en'
    18. );
    19. console.log(message); // "Marshall Sahlins invites Bronisław Malinowski to the party."
    20. });

    Note: This feature requires appropriate CLDR data to have been loaded into the application.

    @dojo/framework/i18n relies on for ICU message formatting, and as such all of the features offered by Globalize.js are available through @dojo/framework/i18n.

    The message formatting examples in the next two subsections will use a with an updated guestInfo message as follows:

    1. export default {
    2. messages: {
    3. guestInfo: `{gender, select,
    4. female {
    5. {guestCount, plural, offset:1
    6. =0 {{host} does not give a party.}
    7. =1 {{host} invites {guest} to her party.}
    8. =2 {{host} invites {guest} and one other person to her party.}
    9. other {{host} invites {guest} and # other people to her party.}}}
    10. male {
    11. {guestCount, plural, offset:1
    12. =0 {{host} does not give a party.}
    13. =1 {{host} invites {guest} to his party.}
    14. =2 {{host} invites {guest} and one other person to his party.}
    15. other {{host} invites {guest} and # other people to his party.}}}
    16. other {
    17. {guestCount, plural, offset:1
    18. =0 {{host} does not give a party.}
    19. =1 {{host} invites {guest} to their party.}
    20. =2 {{host} invites {guest} and one other person to their party.}
    21. other {{host} invites {guest} and # other people to their party.}}}}`
    22. }
    23. };

    ICU message formatting in widgets

    can use the format function returned from their localizeBundle method to perform ICU message formatting in the same way as for described above.

    The ICU-formatted guestInfo message can then be rendered as:

    Direct ICU message formatting

    The ICU-formatted guestInfo message can be converted directly with formatMessage, or getMessageFormatter can be used to generate a function that can be called several times with different options. Note that the formatters created and used by both methods are cached, so there is no performance penalty from compiling the same message multiple times.

    Since the Globalize.js formatting methods use message paths rather than the message strings themselves, the @dojo/framework/i18n methods also require that the bundle itself be provided, so its unique identifier can be resolved to a message path within the Globalize.js ecosystem. If an optional locale is provided, then the corresponding locale-specific message will be used. Otherwise, the current locale is assumed.

    1. import i18n, { formatMessage, getMessageFormatter } from '@dojo/framework/i18n/i18n';
    2. import bundle from 'nls/main';
    3. // 1. Load the messages for the locale.
    4. i18n(bundle, 'en').then(() => {
    5. const message = formatMessage(
    6. bundle,
    7. 'guestInfo',
    8. {
    9. host: 'Margaret Mead',
    10. gender: 'female',
    11. guest: 'Laura Nader',
    12. guestCount: 20
    13. },
    14. 'en'
    15. );
    16. console.log(message); // "Margaret Mead invites Laura Nader and 19 other people to her party."
    17. const formatter = getMessageFormatter(bundle, 'guestInfo', 'en');
    18. console.log(
    19. formatter({
    20. host: 'Margaret Mead',
    21. guest: 'Laura Nader',
    22. guestCount: 20
    23. })
    24. ); // "Margaret Mead invites Laura Nader and 19 other people to her party."
    25. console.log(
    26. formatter({
    27. host: 'Marshall Sahlins',
    28. guest: 'Bronisław Malinowski'
    29. })
    30. ); // "Marshall Sahlins invites Bronisław Malinowski to his party."
    31. });

    Note: This feature requires appropriate to have been loaded into the application.

    As with the message formatting capabilities, @dojo/framework/i18n relies on Globalize.js to provide locale-specific formatting for dates, times, currencies, numbers, and units. The formatters themselves are essentially light wrappers around their Globalize.js counterparts, which helps maintain consistency with the Dojo ecosystem and prevents the need to work with the Globalize object directly. Unlike the message formatters, the date, number, and unit formatters are not cached, as they have a more complex set of options. As such, executing the various “get formatter” methods multiple times with the same inputs does not return the exact same function object.

    @dojo/framework/i18n groups the various formatters accordingly: date and time formatters (@dojo/framework/i18n/date); number, currency, and pluralization formatters (@dojo/framework/i18n/number); and unit formatters (@dojo/framework/i18n/unit). Each method corresponds to a Globalize.js method (see below), and each method follows the same basic format: the last argument is an optional locale, and the penultimate argument is the method options. If specifying a locale but no options, pass null as the options argument. If no locale is provided, then the current (i18n.locale) is assumed.

    1. import { formatDate, getDateFormatter, formatRelativeTime } from '@dojo/framework/i18n/date';
    2. import { formatCurrency, getCurrencyFormatter } from '@dojo/framework/i18n/number';
    3. import { formatUnit, getUnitFormatter } from '@dojo/framework/i18n/unit';
    4. const date = new Date(1815, 11, 10, 11, 27);
    5. // Assume the current locale is "en"
    6. const enDateFormatter = getDateFormatter({ datetime: 'medium' });
    7. enDateFormatter(date); // Dec 10, 1815, 11:27:00 AM
    8. formatDate(date, { date: 'short' }); // 12/10/15
    9. const frDateFormatter = getDateFormatter({ datetime: 'medium' }, 'fr');
    10. frDateFormatter(date); // 10 déc. 1815 à 11:27:00
    11. formatDate(date, { date: 'short' }, 'fr'); // 10/12/1815
    12. formatRelativeTime(-1, 'week'); // "last week"
    13. formatRelativeTime(-1, 'week', { form: 'short' }); // "last wk."
    14. formatRelativeTime(-3, 'week', null, 'fr'); // "il y a 3 semaines"
    15. formatRelativeTime(-3, 'week', { form: 'short' }, 'fr'); // "il y a 3 sem."
    16. const enCurrencyFormatter = getCurrencyFormatter('USD', { style: 'code' });
    17. enCurrencyFormatter(1234.56); // "1,234.56 USD"
    18. formatCurrency(12345.56, 'USD', { style: 'code' }); // "1,234.56 USD"
    19. const frCurrencyFormatter = getCurrencyFormatter('EUR', { style: 'code' }, 'fr');
    20. frCurrencyFormatter(1234.56); // "1 234,56 EUR"
    21. formatCurrency(12345.56, 'EUR', { style: 'code' }, 'fr'); // "1 234,56 EUR"
    22. const enUnitFormatter = getUnitFormatter('feet', { form: 'narrow' });
    23. enUnitFormatter(5280); // 5,280′
    24. formatUnit(5280, 'feet', { form: 'narrow' }); // 5,280′
    25. const frUnitFormatter = getUnitFormatter('meter', null, 'fr');
    26. frUnitFormatter(1000); // 1 000 mètres'
    27. formatUnit(1000, 'meter', null, 'fr); // 1 000 mètres'

    @dojo/framework/i18n/date methods:

    @dojo/framework/i18n/number methods:

    @dojo/framework/i18n/unit methods: