Categories
JavaScript

Formatting Numbers in JavaScript with the NumberFormat Constructor

JavaScript has great internationalization features. One of them is its ability to format numbers for non-English users.

This means that we can display numbers for people using non-English locales without adding another library to do this. We can format numbers with the Intl.NumberFormat constructor. The constructor takes 2 arguments.

The first argument is the locale string or an array of locale strings. Locale strings should be BCP 47 language tags with Unicode key extensions optionally attached. The object created by the constructor has a format method which takes a number that we want to format.

Constructor and Format Method

To use the Intl.NumberFormat constructor, we can create an object with the constructor and then use the format method on the newly created object from the constructor to format the number. We can write something like the following code:

console.log(new Intl.NumberFormat('en', {
  style: 'currency',
  currency: 'GBP'
}).format(222));

The code above formats the number 222 into a price amount denominated in the British Pound. We did that by passing in the style option with the currency value, and the currency property set to GBP, which is the currency symbol for the British Pound.

The Intl.NumberFormat constructor takes 2 arguments, first is the locales argument, which takes one locale string or an array of locale strings. This is an optional argument. It takes a BCP 47 language tag with the optional Unicode extension key nu to specify the numbering system to format the number to. Possible values for nu include: "arab", "arabext", "bali", "beng", "deva", "fullwide", "gujr", "guru", "hanidec", "khmr", "knda", "laoo", "latn", "limb", "mlym", "mong", "mymr", "orya", "tamldec", "telu", "thai", "tibt".

The instance of the object created by the constructor has the format method returns a string with the formatted number. An abridged list of BCP 47 language tags include:

  • ar — Arabic
  • bg — Bulgarian
  • ca — Catalan
  • zh-Hans — Chinese, Han (Simplified variant)
  • cs — Czech
  • da — Danish
  • de — German
  • el — Modern Greek (1453 and later)
  • en — English
  • es — Spanish
  • fi — Finnish
  • fr — French
  • he — Hebrew
  • hu — Hungarian
  • is — Icelandic
  • it — Italian
  • ja — Japanese
  • ko — Korean
  • nl — Dutch
  • no — Norwegian
  • pl — Polish
  • pt — Portuguese
  • rm — Romansh
  • ro — Romanian
  • ru — Russian
  • hr — Croatian
  • sk — Slovak
  • sq — Albanian
  • sv — Swedish
  • th — Thai
  • tr — Turkish
  • ur — Urdu
  • id — Indonesian
  • uk — Ukrainian
  • be — Belarusian
  • sl — Slovenian
  • et — Estonian
  • lv — Latvian
  • lt — Lithuanian
  • tg — Tajik
  • fa — Persian
  • vi — Vietnamese
  • hy — Armenian
  • az — Azerbaijani
  • eu — Basque
  • hsb — Upper Sorbian
  • mk — Macedonian
  • tn — Tswana
  • xh — Xhosa
  • zu — Zulu
  • af — Afrikaans
  • ka — Georgian
  • fo — Faroese
  • hi — Hindi
  • mt — Maltese
  • se — Northern Sami
  • ga — Irish
  • ms — Malay (macrolanguage)
  • kk — Kazakh
  • ky — Kirghiz
  • sw — Swahili (macrolanguage)
  • tk — Turkmen
  • uz — Uzbek
  • tt — Tatar
  • bn — Bengali
  • pa — Panjabi
  • gu — Gujarati
  • or — Oriya
  • ta — Tamil
  • te — Telugu
  • kn — Kannada
  • ml — Malayalam
  • as — Assamese
  • mr — Marathi
  • sa — Sanskrit
  • mn — Mongolian
  • bo — Tibetan
  • cy — Welsh
  • km — Central Khmer
  • lo — Lao
  • gl — Galician
  • kok — Konkani (macrolanguage)
  • syr — Syriac
  • si — Sinhala
  • iu — Inuktitut
  • am — Amharic
  • tzm — Central Atlas Tamazight
  • ne — Nepali
  • fy — Western Frisian
  • ps — Pushto
  • fil — Filipino
  • dv — Dhivehi
  • ha — Hausa
  • yo — Yoruba
  • quz — Cusco Quechua
  • nso — Pedi
  • ba — Bashkir
  • lb — Luxembourgish
  • kl — Kalaallisut
  • ig — Igbo
  • ii — Sichuan Yi
  • arn — Mapudungun
  • moh — Mohawk
  • br — Breton
  • ug — Uighur
  • mi — Maori
  • oc — Occitan (post 1500)
  • co — Corsican
  • gsw — Swiss German
  • sah — Yakut
  • qut — Guatemala
  • rw — Kinyarwanda
  • wo — Wolof
  • prs — Dari
  • gd — Scottish Gaelic

The second argument accepts an object with a few properties — localeMatcher, style, unitDisplay, currency,useGrouping, minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, minimumSignificantDigits, and maximumSignificantDigits .

The localeMatcher option specifies the locale matching algorithm to use. The possible values are lookup and best fit . The lookup algorithm search for the locale until it finds the one that fits the character set of the strings that are being compared. best fit finds the locale that is at least but possibly more suited than the lookup algorithm.

The style option specifies the formatting style to use. Possible values for the style option include decimal , currency , percent , and unit . decimal is the default option and it’s used for plain number formatting, currency is for currency formatting, percent is for percent formatting, and unit is for unit formatting.

The currency option is for use in currency formatting. Possible values are ISO 4217 currency codes, such as USD for the US dollar and EUR for Euro. There’s no default value.

If the style property is set to currency then the currency property must be provided.

The currencyDisplay property sets how the currency is displayed in currency formatting. Possible values are symbol for adding localized currency symbol and it’s the default value, code is for adding the ISO currency code, name to use a localized currency name like ‘dollar’. useGrouping option is for setting the grouping separator to use for numbers, it’s a boolean value.

minimumIntegerDigits, minimumFractionDigits, and maximumFractionDigits are considered one group of options. minimumIntegerDigits specifies the minimum number of integer digits to use, ranging from 1 to 21, with 1 being the default option. minimumFractionDigits is the minimum number of fraction digits to use, ranging from 0 to 20.

The default is 0 for plain number and percent formatting. The default for currency formatting is specified by the ISO 4217 currency code list, and 2 if it’s not specified in the list. maximumFractionDigits is the maximum number of fraction digits to use, with possible values ranging from 0 to 20.

The default for a plain number is the maximum between minimumFractionDigits and 3. The default for currency formatting is the maximum between minimumFractionDigits and the number of fractional unit digits provided by the ISO 4217 currency code list or 2 if the list doesn’t provide that information. The default for percent formatting is the maximum between minimumFractionDigits and 0.

minimumSignificantDigits and maximumSignificantDigits are considered as another group of options. If at least one of the options in this group is defined, then the first group is ignored. minimumSignificantDigits is the minimum number of significant digits to use, with possible values ranging from 1 to 21 with the default being 1. maximumSignificantDigits is the maximum number of significant digits to use, with possible values ranging from 1 to 21 with the default being 21.

Some examples of formatting numbers include requiring a minimum number of digits for a number. We can do that with the constructor and the format method like the following:

console.log(new Intl.NumberFormat('en', {
  style: 'currency',
  currency: 'GBP',
  minimumIntegerDigits: 5
}).format(222));

Then we get £00,222.00 when we run the console.log function in the code above. We can also specify the minimum number of decimals after the decimal point with the minimumFractionDigits option like in the following code:

console.log(new Intl.NumberFormat('en', {
  style: 'currency',
  currency: 'GBP',
  minimumFractionDigits: 2
}).format(222));

Then we get £222.00 when we run the console.log function in the code above. We can change the grouping of the digits with the useGrouping option like in the code below:

console.log(new Intl.NumberFormat('hi', {
  style: 'decimal',
  useGrouping: true
}).format(22222222222));

We can see that we get 22,22,22,22,222 when we log the output of the code above. The hi locale is the Hindi locale, which formats numbers differently than English, and we can see that in Hindi, digits are grouped into groups of 2 when a number is bigger than one thousand.

Also, we can format numbers into non-Arab numerals. For example, if we want to display numbers in Chinese, we can set the nu option as the Unicode extension key of the locale string. For example, we can write:

console.log(new Intl.NumberFormat('zh-Hant-CN-u-nu-hanidec', {
  style: 'decimal',
  useGrouping: true
}).format(12345678));

Then we get ‘一二,三四五,六七八’ logged. The nu-hanidec specified the number system that we want to display the formatted number in. In the example above, we specified the number system to be the Chinese number system, so we displayed all the digits in Chinese.

Other Methods

In addition to the format method, the formatToParts and resolvedOptions methods are also available in the object created by the Intl.NumberFormat constructor. The formatToParts method returns the parts of the formatted number as an array. The resolvedOptions method returns a new object that has the options for formatting the number with properties reflecting the locale and collation options that are computed during the instantiation of the object.

To use the formatToParts method, we can write the following code:

console.log(new Intl.NumberFormat('hi', {
  style: 'decimal',
  useGrouping: true
}).`formatToParts`(22222222222));

Then we get:

[
  {
    "type": "integer",
    "value": "22"
  },
  {
    "type": "group",
    "value": ","
  },
  {
    "type": "integer",
    "value": "22"
  },
  {
    "type": "group",
    "value": ","
  },
  {
    "type": "integer",
    "value": "22"
  },
  {
    "type": "group",
    "value": ","
  },
  {
    "type": "integer",
    "value": "22"
  },
  {
    "type": "group",
    "value": ","
  },
  {
    "type": "integer",
    "value": "222"
  }
]

logged since the formatted number — 22,22,22,22,222 , is broken up into parts and put into the array and returned.

To use the resolvedOptions method, we can write the following code:

const formatOptions = new Intl.NumberFormat('hi', {
  style: 'decimal',
  useGrouping: true
}).resolvedOptions(22222222222)
console.log(formatOptions);

This will return:

{
  "locale": "hi",
  "numberingSystem": "latn",
  "style": "decimal",
  "minimumIntegerDigits": 1,
  "minimumFractionDigits": 0,
  "maximumFractionDigits": 3,
  "useGrouping": true,
  "notation": "standard",
  "signDisplay": "auto"
}

in the console.log output.The code above will get us all the options we used the format the number 22222222222 above.

JavaScript has the ability to format numbers for non-English users with Intl.NumberFormat constructor. This means that we can display numbers for people using non-English locales without adding another library to do this. We can format numbers with the Intl.NumberFormat constructor. The constructor takes 2 arguments.

The first argument is the locale string or an array of locale strings. Locale strings should be BCP 47 language tags with Unicode key extensions optionally attached. The object created by the constructor has a format method that takes a number that we want to format.

It will automatically group digits for different locales when we allow grouping or we can turn the grouping off, and we can specify the number of fractional digits, significant digits, and integer digits to display.

Categories
JavaScript

Formatting Language-Sensitive Lists in JavaScript with ListFormat

With the Intl.ListFormat constructor, we can format language-sensitive lists with ease. It lets us set the locale in which to format a list. Also, it lets us set options for formatting lists such as the type of list or style of the list. To use it, we can create a new ListFormat object with the Intl.ListFormat constructor.

The constructor takes up to two arguments. The first argument is a string of the locale that you want to format the list for. The second argument takes an object with the options for formatting and styling the list. An example use of the Intl.ListFormat constructor is:

const arr = ['bus', 'car', 'train'];
const formatter = new Intl.ListFormat('en', {
  style: 'long',
  type: 'conjunction'
});
const formattedList = formatter.format(arr);
console.log(formattedList);

If we run the code above, we will see bus, car, and train logged from the console.log statement. The Intl.ListFormat constructor creates a formatter object that has the format method to convert an array into a list according to the locale and formatting options that we set.

In the example above, we set the locale to en for English, we set the style property to long, which formats an array of strings into something like A, B, and C or A, B, or C. The type property specifies the kind of list we want to format the list into. Conjunction means that we format a list into A, B, and C.

The arguments of the constructor are the locales that you want to format the list into. It can be a string or an array of strings containing the locale of the list you want to format it into. The locale string or strings should be a BCP 47 language tag. The locales argument is optional. An abridged list of BCP-47 language tags includes:

ar — Arabic

bg — Bulgarian

ca — Catalan

zh-Hans — Chinese, Han (Simplified variant)

cs — Czech

da — Danish

de — German

el — Modern Greek (1453 and later)

en — English

es — Spanish

fi — Finnish

fr — French

he — Hebrew

hu — Hungarian

is — Icelandic

it — Italian

ja — Japanese

ko — Korean

nl — Dutch

no — Norwegian

pl — Polish

pt — Portuguese

rm — Romansh

ro — Romanian

ru — Russian

hr — Croatian

sk — Slovak

sq — Albanian

sv — Swedish

th — Thai

tr — Turkish

ur — Urdu

id — Indonesian

The second argument for the constructor is an object that lets us set the options for how to format the list’s string. There are three properties for this object: localeMatcher, type, and style. The localeMatcher option specifies the locale-matching algorithm to use. The possible values are lookup and best fit.

The lookup algorithm searches for the locale until it finds the one that fits the character set of the strings that are being compared.

best fit finds the locale that is at least as, but possibly more, suitable than the lookup algorithm.

The type property can take on two possible values: conjunction, disjunction, or unit. conjunction means that the list is joined with an and, as in A, B, and C. This is the default option. disnjunction means the list is joined with an or, as in A, B, or C.

unit stands for a list of values with units.

The style property specifies the length of the formatted message. There are two possible values for this property. It can either be long (e.g., A, B, and C), short (e.g., A, B, C), or narrow (e.g., A B C). When style is short or narrow, unit is the only allowed value for this option.

For example, we can use it to format a list into a string that is joined with an and at the end. We can write the following to do so:

const arr = ['bus', 'car', 'bicycle'];
const formatter = new Intl.ListFormat('en', {
  style: 'long',
  type: 'conjunction'
});
const formattedList = formatter.format(arr);
console.log(formattedList);

If we log the formattedList constant like we did above, we get bus, car, and bicycle. We set the en locale to format the string for the English locale, with style long and as a conjunction, which means the list will be joined with an and at the end. If we want to get an or-based list, we can change the type property to a disjunction, as in the following code:

const arr = ['bus', 'car', 'bicycle'];
const formatter = new Intl.ListFormat('en', {
  style: 'long',
  type: 'disjunction'
});
const formattedList = formatter.format(arr);
console.log(formattedList);

If we run the code above, then we get bus, car, or bicycle logged in the console.log statement above.

We can convert it to a shorter list by using the short or narrow option for the style property. For example, we can write:

const arr = ['bus', 'car', 'bicycle'];
const formatter = new Intl.ListFormat('en', {
  style: 'short',
  type: 'conjunction'
});
const formattedList = formatter.format(arr);
console.log(formattedList);

Then we get bus, car, & bicycle in the console.log output when we run the code above. The short and disjunction combination is the same as the long and disjunction combination. If we write:

const arr = ['bus', 'car', 'bicycle'];
const formatter = new Intl.ListFormat('en', {
  style: 'short',
  type: 'disjunction'
});
const formattedList = formatter.format(arr);
console.log(formattedList);

Then we get bus, car, or bicycle. The narrow option would make it even shorter. For example, if we put:

const arr = ['bus', 'car', 'bicycle'];
const formatter = new Intl.ListFormat('en', {
  style: 'narrow',
  type: 'conjunction'
});
const formattedList = formatter.format(arr);
console.log(formattedList);

Then we get bus, car, bicycle logged in the console.log when we run the code above.

This also works for non-English locales. For example, if we want to format a list of Chinese strings into a list, we can write the following code:

const arr = ['日', '月', '星'];
const formatter = new Intl.ListFormat('zh-hant', {
  style: 'narrow',
  type: 'conjunction'
});
const formattedList = formatter.format(arr);
console.log(formattedList);

Then we get ‘日、月和星’, which means sun, moon, and stars. If we switch the style option to long or short, we would get the same thing because in Chinese there’s only one way to write a conjunction, unlike in English. disjunction also works with Chinese. For instance, if we have:

const arr = ['日', '月', '星'];
const formatter = new Intl.ListFormat('zh-hant', {
  style: 'long',
  type: 'disjunction'
});
const formattedList = formatter.format(arr);
console.log(formattedList);

Then we get ‘日、月或星’ which means sun, moon, or stars. If we switch the style option to long or short, as with Chinese conjunctions, we would get the same thing because in Chinese there’s only one way to write a disjunction, unlike in English.


formatToParts() Method

In addition to the format method, the Intl.ListFormat instance also has the formatToParts method, which formats an array into a conjunction or disjunction string and then returns it as an array of the parts of the formatted string.

For example, if we want to return an array of the parts of the formatted English string for a list, then we can write the following code:

const arr = ['bus', 'car', 'bicycle'];
const formatter = new Intl.ListFormat('en', {
  style: 'long',
  type: 'conjunction'
});
const formattedList = formatter.formatToParts(arr);
console.log(formattedList);

Then we get:

[
  {
    "type": "element",
    "value": "bus"
  },
  {
    "type": "literal",
    "value": ", "
  },
  {
    "type": "element",
    "value": "car"
  },
  {
    "type": "literal",
    "value": ", and "
  },
  {
    "type": "element",
    "value": "bicycle"
  }
]

from the console.log statement. These are the parts of the formatted string that we get with the format method, but broken apart into individual entries. This method is handy if we only want some parts of the formatted string.


With the Intl.ListFormat constructor, formatting language-sensitive lists is easy. The constructor takes a locale string or an array of locale strings as the first argument and an object with some options for the second argument. We can convert an array into a string that has the list formatted into conjunction or disjunction with the Intl.ListFormat instance’s format method, which takes a locale string and options for the length style of the formatted string and whether it’s a conjunction, disjunction, or unit string. It also has a formatToParts method to convert it to a formatted list and then break up the parts into an array.

Categories
JavaScript

Formatting Dates With the DateTimeFormat Object

Different parts of the world have different date formats. To deal with this, JavaScript has the Intl.DateTimeFormat constructor to let us format dates into different formats according to different locales.

This means that we can format dates for different places without manipulating date strings ourselves, making our lives much easier. The Intl.DateTimeFormat constructor takes a locale string as the argument.

With the DateTimeFormat constructed with the constructor, we can use the format instance method, which takes in a Date object to return a date string with the date formatted for the locale you specified in the Intl.DateTimeFormat constructor.


The Constructor and the Format Method

To use the Intl.DateTimeFormat constructor, we can use it like in the following example:

const date = new Date(2019, 0, 1, 0, 0, 0);
console.log(new Intl.DateTimeFormat('en-US').format(date));
console.log(new Intl.DateTimeFormat('fr-ca').format(date));
console.log(new Intl.DateTimeFormat(['ban', 'de']).format(date));

In the above example, we created the Date object, and then we used the Intl.DateTimeFormat constructor with one or more locales passed in as a string.

Then, we called format by passing in the Date object. The first example would log 1/1/2019 since the United States use the MM/DD/YYYY date format.

The second example would log 2019–01–01 since French Canadian dates are in YYYY-MM-DD format. The third example takes an array where there are multiple locales, the ones further to the right are the fallback locales for the ones for the left.

In our example, since we have an invalid locale string ban in the first entry of the array, we use the German date format de instead, to format the Date object, so we get 1.1.2019 since Germany uses the DD.MM.YYYY date format.

As we can see from the examples above, the Intl.DateTimeFormat constructor takes a locale string or an array of locale strings. It also accepts Unicode extension keys for locale strings, so that we can append them to our language tag.

The extension keys nu for setting the numbering system, ca for the calendar to format the date, and hc for the hour cycle are supported.

nu’s possible values can be arab, arabext, bali, beng, deva, fullwide, gujr, guru, hanidec, khmr, knda, laoo, latn, limb, mlym, mong, mymr, orya, tamldec, telu, thai, tibt.

ca’s possible values include buddhist, chinese, coptic, ethiopia, ethiopic, gregory, hebrew, indian, islamic, iso8601, japanese, persian, roc.

hc’s possible values include h11, h12, h23, h24. For example, to format dates according to the Buddhist calendar, we can write:

const date = new Date(2019, 0, 1, 0, 0, 0);
console.log(new Intl.DateTimeFormat('en-US-u-ca-buddhist').format(date));

Then we get 1/1/2562 in the console.log since the Buddhist calendar’s year 0 is at 545 BC.

The second argument takes an object that lets us set various options in its properties to format the date.

The localeMatcher option specifies the locale-matching algorithm to use. The possible values are lookup and best fit. The lookup algorithm searches for the locale until it finds the one that fits the character set of the strings that are being compared.

best fit finds the locale that is at least, but possibly more, suited than the lookup algorithm. The timeZone option lets us set the time zone which formats the date.

The most basic implementation only recognizes UTC, but others may recognize IANA time zone names in the IANA time zone data database, like Asia/Shanghai, Asia/Kolkata, America/New_York.

The hour12 option specifies whether the format is for 12-hour time or 24-hour time. The default is locale dependent. It overrides the hc language tag in the first argument.

It’s a true or false value where true means format the date time with 12-hour time. The hourCycle option has possible values of h11, h12, h23, h24 and overrides the hc language tag. hour12 takes precedence over this option if it’s specified.

The formatMatcher property specifies the matching algorithm to use. Possible values are basic and best fit. best fit is the default value.

The following subsets of the date time are required to match the date object to the correct format:

  • weekday, year, month, day, hour, minute, second
  • weekday, year, month, day
  • year, month, day
  • year, month
  • month, day
  • hour, minute, second
  • hour, minute

weekday represents the weekday value, possible values are:

  • long (e.g., Friday)
  • short (e.g., Fri)
  • “narrow" (e.g., T). Note that two weekdays may have the same narrow style for some locales. For example, Tuesday‘s narrow style is also T.

era represents the era, possible values are:

  • long (e.g., Anno Domini)
  • short (e.g., AD)
  • narrow (e.g., A)

year represents the year. The possible values are:

  • numeric (e.g., 2019)
  • 2-digit (e.g., 19)

month is the representation of the month. The possible values are:

  • numeric (e.g., 2)
  • 2-digit (e.g., 02)
  • long (e.g., February)
  • short (e.g., Feb)
  • narrow (e.g., M). Note that two months may have the same narrow style for some locales (e.g. May‘s narrow style is also M).

day represents the day. Possible values are:

  • numeric (e.g., 2)
  • 2-digit (e.g., 02)

hour is the representation of the hour. The possible values are numeric, 2-digit.

minute is the representation of the minute. The possible values are numeric, 2-digit.

second is the representation of the second. The possible values are numeric, 2-digit.

timeZoneName is the representation of the time zone name. The possible values are:

  • long (e.g., Pacific Standard Time)
  • short (e.g., GMT-8)

The default value for each component property above is undefined, but if all components are undefined then the year, month, and day are assumed to be numeric.

An example for using the options is below:

const date = new Date(2019, 0, 1, 0, 0, 0);
const options = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
};
console.log(new Intl.DateTimeFormat('en-ca', options).format(date));

In the code above, we set the locale to Canadian English, and in the options object we set weekday to long, year to numeric, month to long, and day to numeric, so that we get the full weekday name in English, year as a number, month with the full name, and day as a number.

The console.log will log “Tuesday, January 1, 2019”. The example above can have a time zone added to it, as in the following example:

const date = new Date(2019, 0, 1, 0, 0, 0);
const options = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  timeZone: 'America/Vancouver',
  timeZoneName: 'long'
};
console.log(new Intl.DateTimeFormat('en-ca', options).format(date));

In the example above, we added the timeZone and timeZoneLong so that we get the time zone displayed, so that we get “Tuesday, January 1, 2019, Pacific Standard Time”.

The Intl.DateTimeFormat constructor handles non-English format equally well. For example, if we want to format a date into Chinese, we can change the locale and keep the options the same as above like in the following code:

const date = new Date(2019, 0, 1, 0, 0, 0);
const options = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  timeZone: 'America/Vancouver',
  timeZoneName: 'long'
};
console.log(new Intl.DateTimeFormat('zh-Hant', options).format(date));

Then, we get “2019年1月1日 星期二 太平洋標準時間”, which is the same as “Tuesday, January 1, 2019, Pacific Standard Time” in traditional Chinese.


Other Instance Methods

Instances of the Intl.DateTimeFormat constructor object also have a few instance methods in addition to the format() method.

It also has the formatToParts() method to return an array of objects representing different parts of the date string. The resolvedOptions() method returns a new object with properties that reflect the locale and formatting options that we computed during the initialization of the object.

The formatRange() method accepts two Date objects as arguments and formats the date range in the most concise way, based on the locale and options provided when we instantiate the DateTimeFormat object.

Finally, it has the formatRangeToParts() method which accepts two Date objects as arguments and formats the date range in the most concise way based on the locale and options provided when we instantiate the DateTimeFormat object and return the date time parts in an array of objects.

For example, if we have the following code that calls the formatRangeToParts() method:

const startDate = new Date(2019, 0, 1, 0, 0, 0);
const endDate = new Date(2019, 0, 2, 0, 0, 0);
const options = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  timeZone: 'America/Vancouver',
  timeZoneName: 'long'
};
const dateRange = new Intl.DateTimeFormat('zh-Hant', options).formatRangeToParts(startDate, endDate)
console.log(dateRange);

Then we get the following date and time parts logged:

[
  {
    "type": "year",
    "value": "2019",
    "source": "startRange"
  },
  {
    "type": "literal",
    "value": "年",
    "source": "startRange"
  },
  {
    "type": "month",
    "value": "1",
    "source": "startRange"
  },
  {
    "type": "literal",
    "value": "月",
    "source": "startRange"
  },
  {
    "type": "day",
    "value": "1",
    "source": "startRange"
  },
  {
    "type": "literal",
    "value": "日 ",
    "source": "startRange"
  },
  {
    "type": "weekday",
    "value": "星期二",
    "source": "startRange"
  },
  {
    "type": "literal",
    "value": " ",
    "source": "startRange"
  },
  {
    "type": "timeZoneName",
    "value": "太平洋標準時間",
    "source": "startRange"
  },
  {
    "type": "literal",
    "value": " – ",
    "source": "shared"
  },
  {
    "type": "year",
    "value": "2019",
    "source": "endRange"
  },
  {
    "type": "literal",
    "value": "年",
    "source": "endRange"
  },
  {
    "type": "month",
    "value": "1",
    "source": "endRange"
  },
  {
    "type": "literal",
    "value": "月",
    "source": "endRange"
  },
  {
    "type": "day",
    "value": "2",
    "source": "endRange"
  },
  {
    "type": "literal",
    "value": "日 ",
    "source": "endRange"
  },
  {
    "type": "weekday",
    "value": "星期三",
    "source": "endRange"
  },
  {
    "type": "literal",
    "value": " ",
    "source": "endRange"
  },
  {
    "type": "timeZoneName",
    "value": "太平洋標準時間",
    "source": "endRange"
  }
]

As we can see from the console output above, we get the date and time parts in each entry of the array, with the parts of the startDate coming first and the endDate parts being in the latter parts of the array.

If we call the formatRange() method, like in the code below:

const startDate = new Date(2019, 0, 1, 0, 0, 0);
const endDate = new Date(2019, 0, 2, 0, 0, 0);
const options = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  timeZone: 'America/Vancouver',
  timeZoneName: 'long'
};
const dateRange = new Intl.DateTimeFormat('en', options).formatRange(startDate, endDate)
console.log(dateRange);

Then we get:

"Tuesday, January 1, 2019, Pacific Standard Time – Wednesday, January 2, 2019, Pacific Standard Time"

From the console.log.

To call the resolvedOptions method, we can write the code below:

const date = new Date(2019, 0, 1, 0, 0, 0);
const options = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  timeZone: 'America/Vancouver',
  timeZoneName: 'long'
};
const resolvedOptions = new Intl.DateTimeFormat('en', options).resolvedOptions(date)
console.log(resolvedOptions);

Then we get the options that we passed in for formatting the date back:

{
  "locale": "en",
  "calendar": "gregory",
  "numberingSystem": "latn",
  "timeZone": "America/Vancouver",
  "weekday": "long",
  "year": "numeric",
  "month": "long",
  "day": "numeric",
  "timeZoneName": "long"
}

In the console.log.

To use the formatToParts() method, we can use it like the format() method, like in the following code:

const date = new Date(2019, 0, 1, 0, 0, 0);
const options = {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  timeZone: 'America/Vancouver',
  timeZoneName: 'long'
};
const dateParts = new Intl.DateTimeFormat('en', options).formatToParts(date)
console.log(dateParts);

Then we get the parts of the formatted date in the console.log like in the output below:

[
  {
    "type": "weekday",
    "value": "Tuesday"
  },
  {
    "type": "literal",
    "value": ", "
  },
  {
    "type": "month",
    "value": "January"
  },
  {
    "type": "literal",
    "value": " "
  },
  {
    "type": "day",
    "value": "1"
  },
  {
    "type": "literal",
    "value": ", "
  },
  {
    "type": "year",
    "value": "2019"
  },
  {
    "type": "literal",
    "value": ", "
  },
  {
    "type": "timeZoneName",
    "value": "Pacific Standard Time"
  }
]

As we can see, the Intl.DateTimeFormat constructor is very useful for formatting dates for different locales. It eliminates a lot of hassle when formatting dates.

The constructor takes one or more locales as the first argument and a variety of options as the second argument.

It can format dates into one string and also format them and break them up into parts. This saves a lot of hassle when formatting dates for different regions since we don’t have to do any date string manipulation.

Categories
React

React-Intl — Message Formatting

Many apps have to be made usable by different users from various parts of the world.

To make this easier, we can use the react-intl to do the internationalization for us.

In this article, we’ll look at how to get started with formatting messages with the react-intl library.

Message Syntax

We can format messages with placeholders and quantities.

For instance, we can write:

import React from "react";
import { IntlProvider, FormattedMessage } from "react-intl";
const messages = {
  en: {
    greeting: `Hello, {name}, you have {itemCount, plural,
      =0 {no items}
      one {# item}
      other {# items}
    }.`
  }
};
export default function App() {
  const [name] = React.useState("james");
  const [itemCount] = React.useState(20);
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedMessage id="greeting" values={{ name, itemCount }} />
      </p>
    </IntlProvider>
  );
}

to create a translation with the placeholders.

We have placeholder for name and itemCount .

The plural rules are defined by:

{itemCount, plural,
  =0 {no items}
  one {# item}
  other {# items}
}

Then we pass in the name and itemCount and they’ll be interpolated.

The id also has to be set.

Default Message

We can set the default message in the FormattedMessage component.

For instance, we can write:

import React from "react";
import { IntlProvider, FormattedMessage } from "react-intl";
const messages = {
  en: {
    greeting: `Hello, {name}.`
  }
};
export default function App() {
  const [name] = React.useState("james");
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedMessage
          id="greeting"
          description="a greeting"
          defaultMessage="Hello, {name}!"
          values={{
            name
          }}
        />
      </p>
    </IntlProvider>
  );
}

We have the defaultMessage prop which is overridden by the message that we defined.

If we remove the greeting message, then it’ll be used:

import React from "react";
import { IntlProvider, FormattedMessage } from "react-intl";
const messages = {
  en: {}
};
export default function App() {
  const [name] = React.useState("james");
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedMessage
          id="greeting"
          description="a greeting"
          defaultMessage="Hello, {name}!"
          values={{
            name
          }}
        />
      </p>
    </IntlProvider>
  );
}

We can also format the translated text with a function:

import React from "react";
import { IntlProvider, FormattedMessage } from "react-intl";
const messages = {
  en: {}
};
export default function App() {
  const [name] = React.useState("james");
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedMessage
          id="greeting"
          description="a greeting"
          defaultMessage="Hello, {name}!"
          values={{
            name
          }}
        >
          {txt => <h1>{txt}</h1>}
        </FormattedMessage>
      </p>
    </IntlProvider>
  );
}

We have a function instead of nothing as the child of FormattedMessage so we’ll see an h1 heading instead of the default style.

Rich Text Formatting

We can divide the text into chunks and format it.

For instance, we can write:

import React from "react";
import { IntlProvider, FormattedMessage } from "react-intl";
const messages = {
  en: {}
};
export default function App() {
  const [name] = React.useState("james");
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedMessage
          id="greeting"
          description="greeting"
          defaultMessage="Hello, <b>{name}</b>"
          values={{
            b: (...chunks) => <b>{chunks}</b>,
            name
          }}
        />
      </p>
    </IntlProvider>
  );
}

to format the bold text by returning our own component.

b is the tag name and it should match the message.

name has the value of the name placeholder.

It can be any XML tag.

We can have more complex formatting:

import React from "react";
import { IntlProvider, FormattedMessage } from "react-intl";
const messages = {
  en: {}
};
export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedMessage
          id="greeting"
          defaultMessage="Go <a>visit our website</a> and <cta>read it</cta>"
          values={{
            a: (...chunks) => (
              <a
                class="external_link"
                target="_blank"
                rel="noopener noreferrer"
                href="https://www.example.com/"
              >
                {chunks}
              </a>
            ),
            cta: (...chunks) => <b>{chunks}</b>
          }}
        />
      </p>
    </IntlProvider>
  );
}

We replace the a and cta tags with our own components.

FormattedDisplayName

We can format the display name with the FormattedDisplayName component.

For example, we can write:

import React from "react";
import { IntlProvider, FormattedDisplayName } from "react-intl";
const messages = {
  en: {}
};
export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedDisplayName type="currency" value="USD" />
      </p>
    </IntlProvider>
  );
}

We can pass in the type and value props to return the full name of the value .

Conclusion

We can display translated and formatted text with the FormattedMessage component.

FormattedDisplayName lets us display the full name of languages and currencies.

Categories
React

Formatting Dates, Numbers, and Lists with React-Intl

Many apps have to be made usable by different users from various parts of the world.

To make this easier, we can use the react-intl to do the internationalization for us.

In this article, we’ll look at how to get started with formatting dates with the react-intl library.

FormattedRelativeTime

We can use the FormattedRelativeTime component to format relative time.

For example, we can write:

import React from "react";
import { IntlProvider, FormattedRelativeTime } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedRelativeTime
          value={1000}
          numeric="auto"
          updateIntervalInSeconds={10}
        />
      </p>
    </IntlProvider>
  );
}

We use the FormattedRelativeTime component with a few props.

value has the relative time value in seconds.

numeric meabns whether we display the item numerically.

updateIntervalInSeconds is the time interval in seconds in which we update the formatted string.

Number Formatting Components

We can format numbers with a few components.

They include the FormattedNumber , FormattedNumberParts and FormattedPlural components.

FormattedNumber

The FormattedNumber complete renders the formatted number into a fragment.

We can customize this as we wish.

For instance, we can use it by writing:

import React from "react";
import { IntlProvider, FormattedNumber } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedNumber
          value={1024}
          style="unit"
          unit="kilobyte"
          unitDisplay="narrow"
        />
      </p>
    </IntlProvider>
  );
}

We use the FormattedNumber component with the value to format.

style is set to unit so that we format it with the unit.

unit is the unit we want to have with the number.

unitDisplay is the style of the unit we display.

We can only pass in units that are allowed by the Inyl.NumberFormat constructor.

narrow means that we display the abbreviation.

So we get:

1,024kB

as a result.

FormattedNumberParts

The FormattedNumberParts component lets us format each part of the number string individually.

For instance, we can write:

import React from "react";
import { IntlProvider, FormattedNumberParts } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedNumberParts value={1024}>
          {parts => (
            <>
              <b>{parts[0].value}</b>
              {parts[1].value}
              <em>{parts[2].value}</em>
            </>
          )}
        </FormattedNumberParts>
      </p>
    </IntlProvider>
  );
}

to format each part of the number.

In our example, the first parts entry is the thousands digit.

The 2nd part is the comma thousand separator.

The 3rd is the remaining digits.

FormattedPlural

We can format plural numbers with the FormattedPlural component.

To use it, we can write:

import React from "react";
import { IntlProvider, FormattedPlural } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedPlural value={10} one="cat" other="cats" />
      </p>
    </IntlProvider>
  );
}

We use the FormattedPlural compoennt to render the singular or plural according to the value that’s passed into the value prop.

List Formatting Components

We can format lists with react-intl.

FormattedList

We can use the FormmatedList component to format lists the way we want.

For instance, we can write:

import React from "react";
import { IntlProvider, FormattedList } from "react-intl";

const messages = {
  en: {
    greeting: "Hello {name}! How's it going?"
  },
  es: {
    greeting: "¡Hola {name}! ¿Cómo te va?"
  },
  fr: {
    greeting: "Bonjour {name}! Comment ça va?"
  },
  de: {
    greeting: "Hallo {name}! Wie geht's?"
  }
};

export default function App() {
  return (
    <IntlProvider locale="en" messages={messages.en}>
      <p>
        <FormattedList type="conjunction" value={["peter", "paul", "mary"]} />
      </p>
    </IntlProvider>
  );
}

to combine the array in the value prop into a string.

We specified that the type of conjunction so that we combine it with ‘and’ or their equivalent on other languages.

Since we set the locale to English, we get:

peter, paul, and mary

Conclusion

We can format relative time, lists, and numbers with react-intl.