國際化相對時間格式化API:Intl.RelativeTimeFormat

justjavac發表於2018-10-23

原文:The Intl.RelativeTimeFormat API
作者:Mathias Bynens(@mathias)

現代 Web 應用程式通常使用“昨天”,“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('zh'); // 或 'zh-Hans-CN'

rtf.format(3.14, 'second');
// → '3.14秒鐘後'

rtf.format(-15, 'minute');
// → '15分鐘前'

rtf.format(8, 'hour');
// → '8小時後'

rtf.format(-2, 'day');
// → '2天前'

rtf.format(3, 'week');
// → '3周後'

rtf.format(-5, 'month');
// → '5個月前'

rtf.format(2, 'quarter');
// → '2個季度後'

rtf.format(-42, 'year');
// → '42年前'
複製程式碼

此外,Intl.RelativeTimeFormat 建構函式還接受一個可選 options 引數,該引數可以對輸出進行細粒度控制。為了說明靈活性,讓我們根據預設設定檢視更多輸出:

// 建立一個簡體中文相對時間格式化示例,使用預設設定。
// 在這個例子中,我們將預設引數顯式的傳進去
const rtf = new Intl.RelativeTimeFormat('zh', {
 localeMatcher: 'best fit', // 其他值: 'lookup'
 style: 'long', // 其他值: 'short' 或 'narrow'
 numeric: 'always', // 其他值: 'auto'
});

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

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

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

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

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

rtf.format(1, 'week');
// → '1周後'
複製程式碼

您可能已經注意到上面的格式化程式生成了字串 '1天前' 而不是 '昨天',還有顯得比較弱智的 '0周後' 而不是 '本週'。發生這種情況是因為預設情況下,格式化程式使用數值進行輸出。

要更改此行為,請將 numeric 選項設定為 'auto'(預設值是 'always'):

const rtf = new Intl.RelativeTimeFormat('zh', { numeric: 'auto' });

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

rtf.format(-2, 'day');
// → '前天'

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

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

rtf.format(2, 'day');
// → '後天'

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

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

rtf.format(1, 'week');
// → '下週'
複製程式碼

Analogous to other Intl classes, Intl.RelativeTimeFormat has a formatToParts method in addition to the format method. Although format covers the most common use case, formatToParts can be helpful if you need access to the individual parts of the generated output:

與其他 Intl 類一樣,Intl.RelativeTimeFormat 除了 format 方法之外,還有一個 formatToParts 方法。雖然 format 涵蓋了最常見的用例,但如果您需要訪問生成的輸出的各個部分,formatToParts 會很有幫助:

const rtf = new Intl.RelativeTimeFormat('zh', { numeric: 'auto' });

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

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

rtf.format(3, 'week');
// → '3周後'

rtf.formatToParts(3, 'week');
// → [
//  { type: 'integer', value: '3', unit: 'week' },
//  { type: 'literal', value: '周後' }
// ]
複製程式碼

有關其餘選項及其行為的詳細資訊,請參閱 API docs in the proposal repository.

結論

Intl.RelativeTimeFormat 預設情況下在 V8 v7.1.179 和 Chrome 71 中可用。隨著此 API 變得更加廣泛可用,您將發現諸如 Moment.jsGlobalizedate-fns 之類的庫,會從程式碼庫中移除對硬編碼 CLDR 資料庫的依賴性,而使用本機相對時間格式化功能,從而提高載入時效能、分析和編譯時效能、執行時效能和記憶體使用。

相關連結

相關文章