background

The Intl object is a namespace of the ECMAScript internationalization API that provides precise string comparison, number formatting, and date and time formatting, as detailed in the Intl specification

The Intl proposal was approved by the ECMA standard three years ago, and most of the apis in the proposal are now supported by browsers, but I still find myself working with a lot of people who don’t understand Intl and write their own functions that have already been implemented natively (and with bugs). This article briefly introduces several very useful apis in Intl from a practical perspective.

Sequence display formatting

In our work, we often encounter scenarios where multiple messages need to be displayed to users. For example, in the form submission verification, if some lines fail to pass the verification, the front end needs to pop up a message indicating that the amount verification of the first, second and third lines fails. Please fill in the information again

// Define the template
const messages = {
  zh: {
    error: 'Line {0} failed to check amount, please fill in again'
  },
  en: {
    error: 'Amount verification failed at line {0}, please re-fill'}},// Define a conversion function to concatenate an array with, or,
const formatList = (list) = >{... }/ / use
$t('error', formatList(errorList))
Copy the code

As you can see, we need to implement a formatList function, which takes into account multiple languages, numbers greater than 3, and trailing concatenation. It’s not a very simple function, and Intl already has it built in

// We define a formatting function globally
const formatList = new Intl.ListFormat(locale, { style: 'long'.type: 'conjunction' });
/ / use
$t('error', formatList(errorList))
/ / the effect
// Lines 1, 2 and 3 failed to check the amount, please fill in again
// Amount verification failed at line 1, 2, and 3, please re-fill
Copy the code

Amount formatting

Amount formatting is a special type of number formatting. For example, to display amount, you need to format thousands and decimal places, for example, to display 1000 to 1,000.00. Most internationalization tools have this function built-in, such as VUE-i18n

But the real scene may be more complicated, such as input box, we need to implement an amount only allow input conforms to the standard digital, digital input is normal, out-of-focus after formatting the amount after format, as shown at the same time, according to the current language environment and choose the currency formatting (currency involved in digital precision, such as the yen no decimal). It is not allowed to input decimal numbers beyond the precision of the currency. The result is as follows

This requirement description may seem a bit complicated, with a lot of boundary cases to consider, but Intl is a very convenient way to implement this function. The main principle is to use IntlIntl.NumberFormatMethod, specific parameters can beRefer to the MDN So let me put an implementation here

// Decimal separator; Small knowledge: Portuguese language decimal place and thousandths are inverted, not considered here
export const decimalSplit = '. ';
// Thousandth separator
const currencySplit = ', ';

const getDecimal = (s: string) = > s.split(decimalSplit)[1] | |' ';
export const getDecimalLength = (s: string) = >
  (getDecimal(s) || { length: 0 }).length;
const joinNumber = (integer: string, decimal: string) = >
  decimal ? `${integer}${decimalSplit}${decimal}` : integer;
export const decode = (s: string) = >
  s
    .replace(new RegExp(currencySplit, 'g'), ' ')
    .replace(new RegExp(`\${decimalSplit}0*$`.'g'), ' ')
    .replace(new RegExp(`(\${decimalSplit}[^0]*)0*$`.'g'), '$1');
export const formatGen = R.curry((currency = 'CNY', value: string) = >
  new Intl.NumberFormat('zh-CN', {
    style: 'currency',
    currency,
  })
    .format(Number(decode(String(value || '0'))))
    .replace(/[^0-9,.-]/g.' '));/ / use
const format = useMemo(() = >formatGen(currency), [currency]); <Input onBlur={(evt: React.FocusEvent<HTMLInputElement>) => { const validValue = format(evt.target.value.trim()); onChange? .(validValue); }} onChange={(evt: React.ChangeEvent<HTMLInputElement>) => { const v = evt.target.value; const [head, tail] = v.split(decimalSplit); const validHead = head.slice(0, maxIntegerLength); const hasDecimal = v.includes(decimalSplit); const decimal = tail || ''; const integer = hasDecimal ? validHead.concat(decimalSplit) : validHead; const validValue = integer .concat(decimal.slice(0, getDecimalLength(format('0')))) .replace(new RegExp(`[^-0-9${decimalSplit}]`, 'g'), ''); innerValue.current = validValue; onChange? .(validValue); }} / >Copy the code

other

Intl provides many other tools, but here are two that I find useful

  • Intl.RelativeTimeFormat

  • Intl.DateTimeFormat