Intl.RelativeTimeFormat

發布於 · 標記為 Intl

現代網路應用程式通常會使用「昨天」、「42 秒前」或「3 個月後」等片語,而非完整的日期和時間戳記。此類相對時間格式化值已變得非常普遍,以致於有許多熱門函式庫實作了以在地化方式格式化這些值的公用函式。(範例包括 Moment.jsGlobalizedate-fns。)

實作在地化相對時間格式化器時,其中一個問題是您需要一份慣用字詞或片語清單 (例如「昨天」或「上個季度」),供您想要支援的每種語言使用。 Unicode CLDR 提供了這些資料,但若要在 JavaScript 中使用這些資料,必須將其嵌入並與其他函式庫程式碼一起運送。遺憾的是,這會增加此類函式庫的套件大小,進而對載入時間、剖析/編譯成本和記憶體消耗產生負面影響。

全新的 Intl.RelativeTimeFormat API 將此負擔轉移給 JavaScript 引擎,而 JavaScript 引擎可以運送地區資料,並讓 JavaScript 開發人員直接使用。Intl.RelativeTimeFormat 能夠在地化格式化相對時間,同時不犧牲效能。

使用範例 #

下列範例示範如何使用英文建立相對時間格式化器。

const rtf = new Intl.RelativeTimeFormat('en');

rtf.format(3.14, 'second');
// → 'in 3.14 seconds'

rtf.format(-15, 'minute');
// → '15 minutes ago'

rtf.format(8, 'hour');
// → 'in 8 hours'

rtf.format(-2, 'day');
// → '2 days ago'

rtf.format(3, 'week');
// → 'in 3 weeks'

rtf.format(-5, 'month');
// → '5 months ago'

rtf.format(2, 'quarter');
// → 'in 2 quarters'

rtf.format(-42, 'year');
// → '42 years ago'

請注意傳遞給 Intl.RelativeTimeFormat 建構函式的引數可以是包含 BCP 47 語言標籤 的字串,或 此類語言標籤的陣列

以下是使用不同語言 (西班牙文) 的範例

const rtf = new Intl.RelativeTimeFormat('es');

rtf.format(3.14, 'second');
// → 'dentro de 3,14 segundos'

rtf.format(-15, 'minute');
// → 'hace 15 minutos'

rtf.format(8, 'hour');
// → 'dentro de 8 horas'

rtf.format(-2, 'day');
// → 'hace 2 días'

rtf.format(3, 'week');
// → 'dentro de 3 semanas'

rtf.format(-5, 'month');
// → 'hace 5 meses'

rtf.format(2, 'quarter');
// → 'dentro de 2 trimestres'

rtf.format(-42, 'year');
// → 'hace 42 años'

此外,Intl.RelativeTimeFormat 建構函式接受一個選用的 options 引數,可精細控制輸出。為了說明其彈性,讓我們看看一些基於預設設定的英文輸出

// Create a relative time formatter for the English language, using the
// default settings (just like before). In this example, the default
// values are explicitly passed in.
const rtf = new Intl.RelativeTimeFormat('en', {
localeMatcher: 'best fit', // other values: 'lookup'
style: 'long', // other values: 'short' or 'narrow'
numeric: 'always', // other values: 'auto'
});

// Now, let’s try some special cases!

rtf.format(-1, 'day');
// → '1 day ago'

rtf.format(0, 'day');
// → 'in 0 days'

rtf.format(1, 'day');
// → 'in 1 day'

rtf.format(-1, 'week');
// → '1 week ago'

rtf.format(0, 'week');
// → 'in 0 weeks'

rtf.format(1, 'week');
// → 'in 1 week'

您可能已注意到,上述格式化器產生了字串 '1 day ago',而非 'yesterday',以及有點奇怪的 'in 0 weeks',而非 'this week'。這是因為預設情況下,格式化器會在輸出中使用數字值。

若要變更此行為,請將 numeric 選項設定為 'auto' (而非隱含預設值 'always')

// Create a relative time formatter for the English language that does
// not always have to use numeric value in the output.
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });

rtf.format(-1, 'day');
// → 'yesterday'

rtf.format(0, 'day');
// → 'today'

rtf.format(1, 'day');
// → 'tomorrow'

rtf.format(-1, 'week');
// → 'last week'

rtf.format(0, 'week');
// → 'this week'

rtf.format(1, 'week');
// → 'next week'

類似於其他 Intl 類別,Intl.RelativeTimeFormat 除了 format 方法之外,還有一個 formatToParts 方法。儘管 format 涵蓋了最常見的使用案例,但如果你需要存取產生的輸出的個別部分,formatToParts 可能會有幫助

// Create a relative time formatter for the English language that does
// not always have to use numeric value in the output.
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });

rtf.format(-1, 'day');
// → 'yesterday'

rtf.formatToParts(-1, 'day');
// → [{ type: 'literal', value: 'yesterday' }]

rtf.format(3, 'week');
// → 'in 3 weeks'

rtf.formatToParts(3, 'week');
// → [{ type: 'literal', value: 'in ' },
// { type: 'integer', value: '3', unit: 'week' },
// { type: 'literal', value: ' weeks' }]

有關其餘選項及其行為的更多資訊,請參閱 提案儲存庫中的 API 文件

結論 #

Intl.RelativeTimeFormat 預設在 V8 v7.1 和 Chrome 71 中提供。隨著此 API 變得更廣泛可用,你會發現 Moment.jsGlobalizedate-fns 等函式庫會放棄對硬式編碼 CLDR 資料庫的依賴,轉而採用原生相對時間格式化功能,從而改善載入時間效能、剖析和編譯時間效能、執行時間效能和記憶體使用量。

Intl.RelativeTimeFormat 支援 #