JavaScript国际化与本地化实战指南

一、Intl对象:现代化格式化工具

Intl对象是JavaScript内置的国际化工具集,提供对语言敏感的字符串比较、数字格式化、日期和时间格式化等功能。

1. 日期格式化

const date = new Date();

// 基本用法
console.log(new Intl.DateTimeFormat('en-US').format(date)); // "5/15/2023"
console.log(new Intl.DateTimeFormat('zh-CN').format(date)); // "2023/5/15"

// 高级配置
const options = {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  weekday: 'long',
  hour: '2-digit',
  minute: '2-digit',
  timeZone: 'Asia/Shanghai'
};
console.log(new Intl.DateTimeFormat('ja-JP', options).format(date));
// "2023年5月15日月曜日 14時30分"

实践建议

  • 优先使用Intl而非手动拼接日期字符串
  • 考虑用户的系统语言设置:navigator.language
  • 复杂场景下可缓存DateTimeFormat实例提升性能

2. 数字与货币格式化

const number = 123456.789;

// 数字格式化
console.log(new Intl.NumberFormat('de-DE').format(number)); // "123.456,789"
console.log(new Intl.NumberFormat('ar-EG').format(number)); // "١٢٣٬٤٥٦٫٧٨٩"

// 货币格式化
console.log(new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(number)); // "$123,456.79"

console.log(new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY'
}).format(number)); // "¥123,457"

实践建议

  • 货币显示应始终与金额对应,避免硬编码货币符号
  • 使用currencyDisplay: 'code'显示ISO货币代码(如"USD")可避免歧义
  • 敏感金额建议配合minimumFractionDigitsmaximumFractionDigits

3. 列表格式化(ES2023新增)

const list = ['React', 'Vue', 'Angular'];
console.log(new Intl.ListFormat('en').format(list)); // "React, Vue, and Angular"
console.log(new Intl.ListFormat('zh').format(list)); // "React、Vue和Angular"

二、多语言资源加载策略

1. 静态资源组织

推荐目录结构:

public/
  locales/
    en/
      common.json
      home.json
    zh-CN/
      common.json
      home.json
    ja/
      common.json
      home.json

2. 动态加载实现

// 简单实现示例
class I18nLoader {
  constructor(defaultLang = 'en') {
    this.lang = defaultLang;
    this.messages = {};
  }

  async load(lang) {
    if (this.messages[lang]) return;
    
    try {
      const response = await fetch(`/locales/${lang}/common.json`);
      this.messages[lang] = await response.json();
    } catch (e) {
      console.error(`Failed to load ${lang} locale`, e);
      if (lang !== 'en') {
        await this.load('en'); // 回退到英语
      }
    }
  }

  t(key) {
    return this.messages[this.lang]?.[key] || key;
  }
}

3. 高级策略优化

图1

实践建议

  • 实现语言包按需加载,减少初始负载
  • 考虑使用Web Workers预加载语言包
  • 对于SPA应用,可将语言包打包到不同chunk
  • 设置合理的缓存策略(ETag/Last-Modified)

三、时区处理与Temporal API

1. 传统Date的问题

// 问题示例
const meeting = new Date('2023-05-20T14:00:00');
console.log(meeting.toString()); // 结果取决于运行环境时区

2. Temporal API解决方案

// 创建时区敏感时间
const meeting = Temporal.ZonedDateTime.from({
  timeZone: 'America/New_York',
  year: 2023,
  month: 5,
  day: 20,
  hour: 14,
  minute: 0
});

// 转换为其他时区
const londonTime = meeting.withTimeZone('Europe/London');
console.log(londonTime.toString());
// 2023-05-20T19:00:00+01:00[Europe/London]

// 计算时间差
const duration = meeting.until(Temporal.Now.zonedDateTimeISO());
console.log(duration.days); // 剩余天数

3. 时区转换实用函数

function convertTimeForUser(date, targetTimeZone) {
  const temporalDate = Temporal.Instant.from(date.toISOString())
    .toZonedDateTimeISO(targetTimeZone);
  
  return new Intl.DateTimeFormat(navigator.language, {
    timeZone: targetTimeZone,
    dateStyle: 'full',
    timeStyle: 'long'
  }).format(temporalDate);
}

// 使用示例
const now = new Date();
console.log(convertTimeForUser(now, 'Asia/Tokyo'));

实践建议

  • 服务器应始终使用UTC时间存储和传输
  • 前端显示时再转换为用户本地时区
  • 对于重要时间点(如会议),同时显示UTC时间和用户本地时间
  • 使用Intl.supportedValuesOf('timeZone')检测支持的时区

四、综合实践方案

1. Vue/React国际化集成示例

// React上下文示例
const I18nContext = createContext();

function App() {
  const [locale, setLocale] = useState('en');
  const [messages, setMessages] = useState({});

  useEffect(() => {
    async function loadMessages() {
      const response = await import(`./locales/${locale}.json`);
      setMessages(response.default);
    }
    loadMessages();
  }, [locale]);

  return (
    <I18nContext.Provider value={{ locale, setLocale, t: (key) => messages[key] || key }}>
      {/* 应用内容 */}
    </I18nContext.Provider>
  );
}

2. 性能优化技巧

  1. 语言包压缩:使用工具如i18next-http-backendloadPath支持压缩版本
  2. 本地缓存:IndexedDB存储常用语言包
  3. 服务端辅助:通过Accept-Language头返回最合适的初始语言包
  4. CDN分发:将语言包部署到CDN边缘节点

3. 测试要点

// 时区敏感测试示例
describe('Time formatting', () => {
  beforeAll(() => {
    jest.useFakeTimers();
    jest.setSystemTime(new Date('2023-01-01T00:00:00Z'));
  });

  it('should format time in Tokyo timezone', () => {
    const result = formatTime('Asia/Tokyo');
    expect(result).toContain('09:00'); // UTC+9
  });
});

结语

现代JavaScript国际化已从简单的字符串替换发展为包含本地化格式、时区处理等完整解决方案。关键点在于:

  1. 始终以用户的语言环境和时区为展示基准
  2. 实现优雅的降级策略(如语言回退)
  3. 性能敏感场景注意资源加载优化
  4. 考虑使用成熟的国际化库(如i18next、formatjs)处理复杂场景

随着Temporal API的逐步普及,JavaScript的日期时间处理将变得更加强大和可靠,值得开发者持续关注。

评论已关闭