🕐

JavaScriptの組み込みAPIのIntlが凄いので紹介してみた。

2024/01/06に公開

はじめに

明けましておめでとうございます。 (一週間遅れ)

この記事はJavaScriptの組み込みAPI Intl の紹介と解説です。

Intl とは?

MDN から引用すると

Intl オブジェクトは、 ECMAScript の国際化 API の名前空間で、言語に依存した文字列の比較、数値の書式化と、日付の書式化を提供します。 Intl オブジェクトは、いくつかのコンストラクターに加え、国際化コンストラクターや他の言語に関する関数に共通する機能へのアクセスを提供します

用はi18nの書式版です。
例を出すより見たほうが早いので実際に機能解説します。

(先に言っておきますが、実はこれバックエンド無しで自然言語処理が出来る優れものです。)

用語解説

localesって何?そもそもnewって何?って人がいるかもしれないので

locales

https://developer.mozilla.org/ja/docs/Glossary/Locale

new

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/new

機能

全9機能(+2)あります。
特に最後の機能は凄いです。


Intl.Collator()

これは言語を考慮した文字のソートをする為の物です。
例えば英語ではaですが、äで表すところもあります。
この場合ソートはUnicode順で行われるので

before

['Z', 'a', 'z', 'ä']

after

['Z', 'a', 'z', 'ä']

Unicode順では大文字は小文字より前に有るのでZは妥当ですが、
zの後にäが来てしまっています。

その場合に使うのがIntl.Collator()です。

console.log(['Z', 'a', 'z', 'ä'].sort(new Intl.Collator('de').compare));
// Expected output: Array ["a", "ä", "z", "Z"]

console.log(['Z', 'a', 'z', 'ä'].sort(new Intl.Collator('sv').compare));
// Expected output: Array ["a", "z", "Z", "ä"]

console.log(
  ['Z', 'a', 'z', 'ä'].sort(
    new Intl.Collator('de', { caseFirst: 'upper' }).compare,
  ),
);
// Expected output: Array ["a", "ä", "Z", "z"]
// by MDN

svはスウェーデン、deはドイツを表します。

new Intl.Collator(locales, options) でインスタンスを作成可能で、compareメソッドがソートの役割をする関数になります。

それをsortメソッドに渡すことでコードの通りのソートが可能です。

optionscaseFirst: 'upper'はなんとなく察せると思いますが、大文字を小文字より優先する物です。

仕様


Intl.DateTimeFormat()

一言で表すと、Dateオブジェクトの超絶強化版です。
ここでは紹介できないくらい多いので簡単にまとめます。

const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
// Results below assume UTC timezone - your results may vary

// Specify default date formatting for language (locale)
console.log(new Intl.DateTimeFormat('en-US').format(date));
// Expected output: "12/20/2020"

// Specify default date formatting for language with a fallback language (in this case Indonesian)
console.log(new Intl.DateTimeFormat(['ban', 'id']).format(date));
// Expected output: "20/12/2020"
// by MDN

new Dateでいつものインスタンスを作成し、
new Intl.DateTimeFormat(locales, options)で生成したインスタンスのformatメソッドに渡します。

日付の表示の差異やGMTの書式など
めんどくさい所を一気に解決してくれます。

仕様


Intl.DisplayNames

これまた凄いです。
国のコードから呼称の翻訳が可能です。
"US"は英語では"United States"ですが
中国では"美國"です。
日本では"アメリカ合衆国"です。
それらの翻訳を可能にします。

const regionNamesInEnglish = new Intl.DisplayNames(['en'], { type: 'region' });
const regionNamesInTraditionalChinese = new Intl.DisplayNames(['zh-Hant'], {
  type: 'region',
});

console.log(regionNamesInEnglish.of('US'));
// Expected output: "United States"

console.log(regionNamesInTraditionalChinese.of('US'));
// Expected output: "美國"
// by MDN

new Intl.DisplayNames(locales[], options);

見ての通り一つ目の引数に翻訳先のlocalesを入れます。
optionstypeには変換する対象を国名か、言語名か、等々
指定可能です。

仕様


Intl.ListFormat()

私がIntlを知るきっかけになったメソッドです。
英語ではapple, banana, lemon and orangeという表記をすると思います。
それらを制御する関数です。

const vehicles = ['Motorcycle', 'Bus', 'Car'];

const formatter = new Intl.ListFormat('en', {
  style: 'long',
  type: 'conjunction',
});
console.log(formatter.format(vehicles));
// Expected output: "Motorcycle, Bus, and Car"

const formatter2 = new Intl.ListFormat('de', {
  style: 'short',
  type: 'disjunction',
});
console.log(formatter2.format(vehicles));
// Expected output: "Motorcycle, Bus oder Car"

const formatter3 = new Intl.ListFormat('en', { style: 'narrow', type: 'unit' });
console.log(formatter3.format(vehicles));
// Expected output: "Motorcycle Bus Car"

new Intl.ListFormat(locales, options);

見ての通りです。
配列に複数の要素を入れ、それらを様々な表現の形式に変換可能です。

仕様


Intl.Locale()

これはイマイチユースケースが分らず、仕様を見ましたがサッパリです。
一意の形式から当てはまるものを取り出すという事でしょうか・・・?

はてなブックマークのコメント欄に
なるほどなと思ったコメントがあったので引用させて頂きます。

sgo2 Intl.LocaleはIntl.Collator等に渡す文字列を事前に検証/正規化しておくのに使えると思えば良いかと。(RegExpとString.replaceの関係みたいなもの)

const korean = new Intl.Locale('ko', {
  script: 'Kore',
  region: 'KR',
  hourCycle: 'h23',
  calendar: 'gregory',
});

const japanese = new Intl.Locale('ja-Jpan-JP-u-ca-japanese-hc-h12');

console.log(korean.baseName, japanese.baseName);
// Expected output: "ko-Kore-KR" "ja-Jpan-JP"

console.log(korean.hourCycle, japanese.hourCycle);
// Expected output: "h23" "h12"

new Intl.Locale(tag, options);

仕様


Intl.NumberFormat()

これもまたまた凄いです。
123,456.789という数のお金の表示で、日本語では¥123,457 (繰り上げ)だし ドイツでは"123.456,79 €" (小数点2桁)

そんな仕様をこれ一つで解決します。

const number = 123456.789;

console.log(
  new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(
    number,
  ),
);
// Expected output: "123.456,79 €"

// The Japanese yen doesn't use a minor unit
console.log(
  new Intl.NumberFormat('ja-JP', { style: 'currency', currency: 'JPY' }).format(
    number,
  ),
);
// Expected output: "¥123,457"

// Limit to three significant digits
console.log(
  new Intl.NumberFormat('en-IN', { maximumSignificantDigits: 3 }).format(
    number,
  ),
);
// Expected output: "1,23,000"

new Intl.NumberFormat(locales, options)

仕様


Intl.PluralRules()

これもまたまた凄く、出来る事が多くて混乱しますが、要は"1" = "one" = "1st" を変換する為の様なものです。 (勿論これだけではありませんが)

これは出来る事が多すぎるので気になれば仕様を読んでください。

仕様

Intl.RelativeTimeFormat()

これも滅茶苦茶凄い
一日前は1 day ago、オプション次第でyesterdayにも
二日前は2 days ago
...

そんな相対時間の変換を超簡単にする為のメソッドです。

勿論 一日後はtomorrowになります。

// Create a relative time formatter in your locale
// with default values explicitly passed in.
const rtf = new Intl.RelativeTimeFormat("en", {
  localeMatcher: "best fit", // other values: "lookup"
  numeric: "always", // other values: "auto"
  style: "long", // other values: "short" or "narrow"
});

// 負の値 (-1) を使った相対時間のフォーマット
rtf.format(-1, "day");
// > "1 day ago"

// 正の値 (1) を使った相対時間のフォーマット
rtf.format(1, "day");
// > "in 1 day"
// by MDN

numeric: "auto"1日前 -> yesterdayの様な事が可能です。

new Intl.RelativeTimeFormat(locales, options)

(仕様)[https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat]




Intl.Segmenter()

一番紹介したかった奴です。
ななめ読みしてる人でも分かるように強調しておきます。
これ一言で言うと自然言語解析がブラウザ上で出来ます。(勿論Deno等でもできます。)

const segmenterJa = new Intl.Segmenter('ja', { granularity: 'word' });
const string1 = 'JavaScriptは、ちょっと分かる。';

const iterator1 = segmenterJa.segment(string1)[Symbol.iterator]();

const segmentsArray = Array.from(iterator1, segment => segment.segment);

console.log(segmentsArray);

new Intl.Segmenter(locales, options)でインスタンス生成、segmentで分割を行います。
イテレーターを返すので、無理やり配列に変換して中身を見ます。

結果は画像の通りですが ['JavaScript', 'は', '、', 'ちょっと', '分かる', '。']
素晴らしい。ブラウザだけでこれが出来ます。

ちなみにoptionsには他の形式も指定可能で、勿論他の言語も可能です。

ちなみにセグメントオブジェクト単体では

{
    "segment": "今日",
    "index": 0,
    "input": "今日は良い天気ですね。",
    "isWordLike": true
}

こうなります。

const segmenter = new Intl.Segmenter('ja', { granularity: 'word' });
const string = '今日は良い天気ですね。';

console.log(segmenter.segment(string).containing());

詳しくは仕様を見てください。
精度を求めるならバックエンドでやるに越した事は有りません

(仕様)[https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/Segmenter]




おまけ

サブみたいなメソッドです。

Intl.getCanonicalLocales

簡単に言うと表記ゆれを修正するみたいな機能です。

console.log(Intl.getCanonicalLocales('EN-US'));
// Expected output: Array ["en-US"]

console.log(Intl.getCanonicalLocales(['EN-US', 'Fr']));
// Expected output: Array ["en-US", "fr"]

try {
  Intl.getCanonicalLocales('EN_US');
} catch (err) {
  console.log(err.toString());
  // Expected output: RangeError: invalid language tag: EN_US
}

仕様

Intl.supportedValuesOf()

対応している地域、コードを返します。

console.log(Intl.supportedValuesOf('calendar'));
console.log(Intl.supportedValuesOf('collation'));
console.log(Intl.supportedValuesOf('currency'));
console.log(Intl.supportedValuesOf('numberingSystem'));
console.log(Intl.supportedValuesOf('timeZone'));
console.log(Intl.supportedValuesOf('unit'));
// Expected output: Array ['key'] (for each key)

try {
  Intl.supportedValuesOf('someInvalidKey');
} catch (err) {
  console.log(err.toString());
  // Expected output: RangeError: invalid key: "someInvalidKey"
}

/**
> @Array ["buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic", "gregory"...]
> @Array ["AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD"...]
> @Array ["adlm", "ahom", "arab", "arabext", "bali", "beng", "bhks", "brah"...]
> @Array ["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers"...]
> @Array ["acre", "bit", "byte", "celsius", "centimeter", "day", "degree", "fahrenheit", "fluid-ounce", "foot"...]
> "@RangeError: Invalid key : someInvalidKey"
*/

仕様

終わりに

ポテンシャルが凄すぎる。
色んなアイデアやユースケースが浮かんできます
良かったらいいねシェアよろしくお願いします。

関連リンク

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl
https://tc39.es/ecma402/#intl-object

Discussion