JavaScriptの標準組み込みオブジェクトにIntlオブジェクトがあります。国際化APIの名前空間で、localeに依存した数値のフォーマットや分かち書きを行うプロパティを持ちます。
この記事ではそんなプロパティの1つであるRelativeTimeFormatについて解説します。
RelativeTimeFormat
RelativeTimeFormatは相対時刻をlocaleに応じて扱うためのオブジェクトです。
const rtf = new Intl.RelativeTimeFormat('ja-JP');
// 2 日後
console.log(rtf.format(2, 'day'));
// 9 か月前
console.log(rtf.format(-9, 'month'));
RelativeTimeFormatを初期化するタイミングで扱いたいlocaleを指定します。
const rtf = new Intl.RelativeTimeFormat('ja-JP');
RelativeTimeFormatには第2引数でオプションをオブジェクトとして渡せます。
const rtf = new Intl.RelativeTimeFormat(
'ja-JP',
{
localeMatcher: 'lookup',
numeric: 'auto',
style: 'short',
},
);
オプションはlocaleMatcherとnumericとstyleの3つです。
localeMathcerは第一引数のlocaleが配列で複数渡ってきた時の優先順位の付け方を決めます。デフォルトではbest fitで他にlookupを指定できます。
best fitはリクエストやランタイムを用いて最適なlocaleを決定し、lookupを指定した場合はLookupのアルゴリズムによって決定されます。
numericはメッセージを表示する形式を指定できます。デフォルトはalwaysでどのような数値であっても一貫した形式でフォーマットするのですが、autoにすると明日や昨日のように特定の数値を別の一般的な言い方に置き換えます。
const rtf1 = new Intl.RelativeTimeFormat('ja-JP');
const rtf2 = new Intl.RelativeTimeFormat(
'ja-JP',
{
numeric: 'auto',
},
);
// 2 日後
console.log(rtf1.format(2, 'day'));
// 明後日
console.log(rtf2.format(2, 'day'));
styleは単位の長さを指定します。デフォルトはlongでshortとnarrowが存在します。
localeがja-JPの場合はlongとshortに違いがありません(例えばlocaleがenの場合はlongではmonth、shortではmoのように表示されます)。narrowは数値と単位の間の空白を埋めた形で出力します。
const rtf1 = new Intl.RelativeTimeFormat('ja-JP');
const rtf2 = new Intl.RelativeTimeFormat(
'ja-JP',
{
style: 'short',
},
);
const rtf3 = new Intl.RelativeTimeFormat(
'ja-JP',
{
style: 'narrow',
},
);
// 2 日後
console.log(rtf1.format(2, 'day'));
// 2 日後
console.log(rtf2.format(2, 'day'));
// 2日後
console.log(rtf3.format(2, 'day'));
このように作成されたRelativeTimeFormatはformatメソッドを用いて相対時刻を表示します。第1引数に相対日付の数値を、第2引数には単位を渡します。
第1引数の数値は正であれば未来との相対時刻を、負であれば過去との相対日付と判断します。
const rtf = new Intl.RelativeTimeFormat('ja-JP');
// 1 年後
console.log(rtf.format(1, 'year'));
// 1 年前
console.log(rtf.format(-1, 'year'));
第2引数はyear、quarter、month、week、day、hour、minute、secondとその複数形を渡します。それぞれその名の通りのフォーマットをしてくれます。
const rtf = new Intl.RelativeTimeFormat('ja-JP');
// 1 年後
console.log(rtf.format(1, 'year'));
// 1 四半期後
console.log(rtf.format(1, 'quarter'));
// 1 か月後
console.log(rtf.format(1, 'month'));
// 1 週間後
console.log(rtf.format(1, 'week'));
// 1 日後
console.log(rtf.format(1, 'day'));
// 1 時間後
console.log(rtf.format(1, 'hour'));
// 1 分後
console.log(rtf.format(1, 'minute'));
// 1 秒後
console.log(rtf.format(1, 'second'));
部品単位で相対時刻を取得する
RelatveTimeFormatオブジェクトのformatToPartsメソッドを使えば数値と単位を配列の要素に分けて表示できます。
const rtf = new Intl.RelativeTimeFormat('ja-JP');
/**
* [
* {
* type: 'integer',
* unit: 'day',
* value: 2,
* },
* {
* type: 'literal',
* value: ' 日後',
* },
* ]
*/
console.log(rtf.formatToParts(2, 'day'));
/**
* [
* {
* type: 'integer',
* unit: 'day',
* value: 9,
* },
* {
* type: 'literal',
* value: ' か月前',
* },
* ]
*/
console.log(rtf.formatToParts(-9, 'month'));
引数はformatメソッドと同じです。単位には空白が含まれていることに注意です。
localeが対応していることを確認する
RelativeTimeFormatオブジェクトに渡すlocaleは静的メソッドであるsupportedLocalesOfを用いて確認できます。引数はRelativeTimeFormatオブジェクトのコンストラクタと同じです。
// ['ja', 'ja-JP']
console.log(
Intl.RelativeTimeFormat.supportedLocalesOf(
['ja', 'JP', 'ja-JP']
)
);
有効なオプションを確認する
resolvedOptionsはRelativeTimeFormatオブジェクトを作成したときに有効となった設定を表示します。
// {locale: 'ja', style: 'long', numeric: 'always', numberingSystem: 'latn'}
console.log((new Intl.RelativeTimeFormat('ja')).resolvedOptions());
// {locale: 'ja-JP', style: 'long', numeric: 'always', numberingSystem: 'latn'}
console.log((new Intl.RelativeTimeFormat(['ja-JP', 'en'])).resolvedOptions());
const rtf = new Intl.RelativeTimeFormat(
'ja-JP',
{
localeMatcher: 'lookup',
numeric: 'auto',
style: 'short',
},
);
// {locale: 'ja-JP', style: 'short', numeric: 'auto', numberingSystem: 'latn'}
console.log(rtf.resolvedOptions());
localeMatcherはおそらくlocaleの決定後は使われないことから表示されません。
numberingSystemだけ見慣れませんが、これはlocaleに依存する命数法を渡しています。
const locale = new Intl.Locale(
'ja-JP',
{ numberingSystem: 'arab' }
);
// ['arab']
console.log(locale.getNumberingSystems());
// {locale: 'ja-JP-u-nu-arab', style: 'long', numeric: 'always', numberingSystem: 'arab'}
console.log((new Intl.RelativeTimeFormat(locale)).resolvedOptions())