大家好!欢迎来到今天的国际化定制小课堂。我是你们的老朋友,今天咱们不聊鸡汤,只啃硬骨头,一起深入 Temporal API
、DateTimeFormat
和 Intl.Locale
的世界,玩转高级国际化定制。
首先,咱们得明确一个核心思想:国际化不仅仅是翻译文本那么简单,它涉及到日期、时间、数字、货币等等各种格式的本地化,让你的应用在不同文化背景下都能像在家一样舒适。
第一部分:Temporal API 的崭新世界
Temporal API
是 JavaScript 中处理日期和时间的新一代 API,旨在替代老旧的 Date
对象,解决其设计上的种种缺陷。它引入了许多新的类型,例如 Temporal.PlainDate
、Temporal.PlainTime
、Temporal.PlainDateTime
、Temporal.ZonedDateTime
等,让我们能更精确、更灵活地处理日期和时间。
-
Temporal.PlainDate
:纯粹的日期Temporal.PlainDate
只包含年、月、日,不涉及时区或时间信息。const plainDate = Temporal.PlainDate.from({ year: 2024, month: 10, day: 27 }); console.log(plainDate.toString()); // 输出: 2024-10-27
-
Temporal.PlainTime
:纯粹的时间Temporal.PlainTime
只包含小时、分钟、秒、毫秒等时间信息,同样不涉及时区。const plainTime = Temporal.PlainTime.from({ hour: 10, minute: 30, second: 0 }); console.log(plainTime.toString()); // 输出: 10:30:00
-
Temporal.PlainDateTime
:日期和时间的组合Temporal.PlainDateTime
是Temporal.PlainDate
和Temporal.PlainTime
的结合体,依然不带时区信息。const plainDateTime = Temporal.PlainDateTime.from({ year: 2024, month: 10, day: 27, hour: 10, minute: 30 }); console.log(plainDateTime.toString()); // 输出: 2024-10-27T10:30:00
-
Temporal.ZonedDateTime
:带时区的日期和时间Temporal.ZonedDateTime
是Temporal API
中最强大的类型之一,它包含了日期、时间和时区信息。const zonedDateTime = Temporal.ZonedDateTime.from({ year: 2024, month: 10, day: 27, hour: 10, minute: 30, timeZone: 'America/Los_Angeles' }); console.log(zonedDateTime.toString()); // 输出类似: 2024-10-27T10:30:00-07:00[America/Los_Angeles]
第二部分:DateTimeFormat
的格式化艺术
Intl.DateTimeFormat
对象是 ECMAScript 国际化 API 的一部分,用于格式化日期和时间,使其符合特定区域设置的约定。
-
基本用法
const date = new Date(); const formatter = new Intl.DateTimeFormat('zh-CN'); // 中国大陆的区域设置 console.log(formatter.format(date)); // 输出类似: 2024/10/27
-
选项配置
Intl.DateTimeFormat
允许你通过选项对象来定制日期和时间的格式。选项 描述 可能的值 localeMatcher
指定用于区域设置匹配的算法。 "best fit"
(默认),"lookup"
timeZone
指定时区。 IANA 时区名称,例如 "America/Los_Angeles"
formatMatcher
指定用于格式化的算法。 "best fit"
(默认),"basic"
hour12
是否使用 12 小时制。 true
,false
(默认,使用 24 小时制)hourCycle
指定 12 小时制的小时周期。 "h11"
(0-11),"h12"
(1-12),"h23"
(0-23),"h24"
(1-24)weekday
星期几的显示方式。 "narrow"
,"short"
,"long"
era
纪元的显示方式。 "narrow"
,"short"
,"long"
year
年份的显示方式。 "numeric"
,"2-digit"
month
月份的显示方式。 "numeric"
,"2-digit"
,"narrow"
,"short"
,"long"
day
日期的显示方式。 "numeric"
,"2-digit"
hour
小时的显示方式。 "numeric"
,"2-digit"
minute
分钟的显示方式。 "numeric"
,"2-digit"
second
秒的显示方式。 "numeric"
,"2-digit"
fractionalSecondDigits
小数秒的位数。 1
,2
,3
timeZoneName
时区名称的显示方式。 "short"
,"long"
,"shortOffset"
,"longOffset"
,"shortGeneric"
,"longGeneric"
calendar
使用的日历系统。 例如 "gregory"
,"chinese"
,"islamic"
numberingSystem
使用的数字系统。 例如 "arab"
,"latn"
const date = new Date(); const options = { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric', timeZone: 'America/Los_Angeles', timeZoneName: 'short' }; const formatter = new Intl.DateTimeFormat('zh-CN', options); console.log(formatter.format(date)); // 输出类似: 2024年10月27日 星期日 上午10:30 PDT
-
formatRange
的妙用formatRange
方法可以格式化日期范围,这在显示事件持续时间或时间跨度时非常有用。const startDate = new Date(2024, 0, 1); // 2024年1月1日 const endDate = new Date(2024, 0, 10); // 2024年1月10日 const formatter = new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }); console.log(formatter.formatRange(startDate, endDate)); // 输出类似: 2024年1月1日 - 2024年1月10日
-
与
Temporal API
结合DateTimeFormat
也能与Temporal API
的类型无缝衔接。const zonedDateTime = Temporal.ZonedDateTime.from({ year: 2024, month: 10, day: 27, hour: 10, minute: 30, timeZone: 'America/Los_Angeles' }); const formatter = new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', timeZoneName: 'short' }); console.log(formatter.format(zonedDateTime)); // 输出类似: 2024年10月27日 上午10:30 PDT
第三部分:Intl.Locale
的区域设置魔法
Intl.Locale
对象表示一个 Unicode 区域设置标识符,它提供了访问区域设置相关信息的接口,例如语言、脚本、国家/地区、变体等。
-
创建
Intl.Locale
对象const locale = new Intl.Locale('zh-CN'); console.log(locale.language); // 输出: zh console.log(locale.script); // 输出: Hans console.log(locale.region); // 输出: CN
-
访问区域设置信息
Intl.Locale
提供了许多属性和方法来访问区域设置信息。属性/方法 描述 language
语言代码。 script
脚本代码。 region
国家/地区代码。 baseName
不包含扩展名和私有使用的基本区域设置名称。 toString()
返回区域设置标识符字符串。 maximize()
返回包含尽可能多信息的区域设置。 minimize()
返回包含尽可能少信息的区域设置。 getTextInfo()
返回文本方向信息。 timeZones
返回与该区域设置关联的时区列表(需要浏览器支持)。 hourCycles
返回该区域设置支持的小时周期列表(需要浏览器支持)。 calendars
返回该区域设置支持的日历系统列表(需要浏览器支持)。 numberingSystems
返回该区域设置支持的数字系统列表(需要浏览器支持)。 const locale = new Intl.Locale('en-US-u-ca-islamic-nu-arab'); console.log(locale.calendar); // 输出: islamic console.log(locale.numberingSystem); // 输出: arab console.log(locale.toString()); // 输出: en-US-u-ca-islamic-nu-arab
-
与
DateTimeFormat
结合,实现更精细的控制Intl.Locale
可以与DateTimeFormat
结合,实现更精细的日期和时间格式控制。const locale = new Intl.Locale('ar-AE-u-ca-islamic-nu-arab'); const options = { year: 'numeric', month: 'long', day: 'numeric', calendar: locale.calendar, numberingSystem: locale.numberingSystem }; const formatter = new Intl.DateTimeFormat(locale.toString(), options); const date = new Date(); console.log(formatter.format(date)); // 输出符合阿拉伯伊斯兰历的日期格式
第四部分:高级定制技巧
-
自定义格式模式
虽然
Intl.DateTimeFormat
提供了丰富的选项,但有时你可能需要更精细的控制。这时,你可以考虑使用第三方库,例如date-fns
或moment.js
(尽管 Moment.js 已经处于维护模式,不推荐用于新项目,但仍然可以借鉴其格式化模式)。这些库允许你使用自定义的格式模式来格式化日期和时间。// 使用 date-fns import { format } from 'date-fns'; import { zhCN } from 'date-fns/locale'; // 引入中文区域设置 const date = new Date(); const formattedDate = format(date, 'yyyy年MM月dd日 EEEE', { locale: zhCN }); console.log(formattedDate); // 输出类似: 2024年10月27日 星期日
-
处理相对时间
相对时间,例如 "几分钟前"、"明天" 等,也是国际化中常见的需求。
Intl.RelativeTimeFormat
对象可以帮助你处理相对时间。const rtf = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' }); console.log(rtf.format(-1, 'day')); // "昨天" console.log(rtf.format(1, 'day')); // "明天" console.log(rtf.format(-3, 'hour')); // "3小时前" console.log(rtf.format(2, 'minute')); // "2分钟后"
-
处理复数形式
不同语言对复数的处理方式不同。
Intl.PluralRules
对象可以帮助你根据数量选择正确的复数形式。const pr = new Intl.PluralRules('en-US'); console.log(pr.select(0)); // "other" console.log(pr.select(1)); // "one" console.log(pr.select(2)); // "other" console.log(pr.select(1.5));// "other" const messages = { one: "There is # message.", other: "There are # messages." }; function format(n, message) { const rule = pr.select(n); const msg = messages[rule] || messages.other; return msg.replace("#", n); } console.log(format(1)); // "There is 1 message." console.log(format(2)); // "There are 2 messages."
-
利用
toLocaleString
简化操作虽然
Intl.DateTimeFormat
是专门用于格式化日期和时间的 API,但 JavaScript 的Date
对象本身也提供了一个toLocaleString
方法,可以方便地进行本地化格式化。const date = new Date(); // 使用默认区域设置 console.log(date.toLocaleString()); // 指定区域设置和选项 const options = { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', timeZone: 'America/Los_Angeles', timeZoneName: 'short' }; console.log(date.toLocaleString('zh-CN', options)); // 输出类似: 2024年10月27日 上午10:30 PDT
toLocaleString
方法在快速实现本地化格式化时非常方便,但如果你需要更精细的控制和更高级的功能,Intl.DateTimeFormat
仍然是更好的选择。
第五部分:实战演练
让我们来做一个简单的例子:一个多语言的事件倒计时器。
<!DOCTYPE html>
<html>
<head>
<title>多语言事件倒计时器</title>
<style>
body {
font-family: sans-serif;
}
</style>
</head>
<body>
<h1>事件倒计时</h1>
<label for="locale">选择语言:</label>
<select id="locale">
<option value="en-US">English (US)</option>
<option value="zh-CN">中文 (中国)</option>
<option value="ja-JP">日本語 (日本)</option>
</select>
<div id="countdown"></div>
<script>
const localeSelect = document.getElementById('locale');
const countdownDiv = document.getElementById('countdown');
const eventDate = new Date('2024-12-25T00:00:00'); // 圣诞节
function updateCountdown() {
const now = new Date();
const diff = eventDate.getTime() - now.getTime();
if (diff <= 0) {
countdownDiv.textContent = '事件已发生!';
return;
}
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
const locale = localeSelect.value;
const dayUnit = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(days, 'day');
const hourUnit = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(hours, 'hour');
const minuteUnit = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(minutes, 'minute');
const secondUnit = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(seconds, 'second');
let countdownText;
if (locale === 'zh-CN') {
countdownText = `${days}天${hours}小时${minutes}分钟${seconds}秒`;
} else if (locale === 'ja-JP') {
countdownText = `${days}日${hours}時間${minutes}分${seconds}秒`;
} else {
countdownText = `${days} days, ${hours} hours, ${minutes} minutes, ${seconds} seconds`;
}
countdownDiv.textContent = countdownText;
}
localeSelect.addEventListener('change', updateCountdown);
setInterval(updateCountdown, 1000); // 每秒更新一次
updateCountdown(); // 初始更新
</script>
</body>
</html>
这个例子演示了如何使用 Intl.RelativeTimeFormat
实现多语言的倒计时显示。你可以根据需要扩展这个例子,添加更多的语言支持和更复杂的格式化。
总结
Temporal API
、DateTimeFormat
和 Intl.Locale
是 JavaScript 国际化工具箱中的强大武器。掌握它们,你就能构建出真正全球化的应用,让用户无论身在何处,都能感受到你的用心。记住,国际化不仅仅是技术问题,更是一种对不同文化的尊重。希望今天的课程能帮助你更好地理解和应用这些 API,在国际化道路上越走越远!
今天的课程就到这里,谢谢大家!希望大家都能成为国际化大师!