i18n-前端国际化实现原理与架构设计

420 阅读3分钟

国际化(Internationalization,简称 i18n)  是指设计、开发产品或服务(尤其是软件、网站、内容、业务流程等)时,使其能够轻松适应不同语言、地区和文化环境的过程和技术。它的核心目标是消除地域和文化障碍,为全球用户提供无障碍的体验。

为什么叫 i18n?
这是一个常见的缩写,源自单词 “Internationalization” 的首字母 I 和末字母 n,中间有 18 个字母,因此简写为 i18n

实现原理

  1. 使用键值对存储不同语言的翻译包
  2. 用一个管理器使用对应的语言进行翻译
  3. 代码中原本写死的中文,用函数替换,参数是语言包对应的key值,返回值就是对应的语言

构架设计

1.整体架构思路

// 架构层次
项目根目录
└── src/
    └── modules/
        └── i18n/
            ├── I18nManager.ts       // 核心管理器
            └── languages/           // 语言包目录
                ├── zh-CN.ts        // 中文语言包
                └── en-US.ts        // 英文语言包

管理器模式 + 语言包的架构:

  • I18nManager: 作为国际化的核心管理器,负责语言包管理、文本翻译、语言切换等功能
  • 语言包: 按语言分离的配置文件,便于维护和扩展
  • 统一导出: 全局单例模式,确保一致性

2.核心管理器设计

type LanguagePack = {
  [key: string]: string | LanguagePack;
};

interface I18nConfig {
  defaultLanguage: string;
}

class I18nManager {
  private static instance: I18nManager;
  private currentLanguage: string;
  private languagePacks: Map<string, LanguagePack> = new Map();

  constructor(config: I18nConfig) {
    this.currentLanguage = config.defaultLanguage;
  }

  static getInstance(config?: I18nConfig): I18nManager {
    if (!I18nManager.instance && config) {
      I18nManager.instance = new I18nManager(config);
    }
    return I18nManager.instance;
  }

  /**
   * 设置语言包
   */
  setLanguagePack(language: string, pack: LanguagePack): void {
    this.languagePacks.set(language, pack);
  }

  /**
   * 获取当前语言
   */
  getCurrentLanguage(): string {
    return this.currentLanguage;
  }

  /**
   * 设置当前语言
   */
  setCurrentLanguage(language: string): void {
    if (this.currentLanguage !== language) {
      this.currentLanguage = language;
    }
  }

  /**
   * 获取文本
   */
  t(key: string, params?: Record<string, string | number>): string {
    const pack = this.languagePacks.get(this.currentLanguage);

    if (!pack) {
      console.warn(`${this.currentLanguage} 语言包没有找到`);
      return key;
    }

    const value = this.getNestedValue(pack, key);
    if (typeof value !== 'string') {
      console.warn(`${key} 翻译键没有找到`);
      return key;
    }

    return this.interpolate(value, params);
  }

  /**
   * 获取嵌套值
   */
  private getNestedValue(obj: any, path: string): any {
    return path.split('.').reduce((current, key) => {
      return current && current[key] !== undefined ? current[key] : undefined;
    }, obj);
  }

  /**
   * 插值替换
   */
  private interpolate(text: string, params?: Record<string, string | number>): string {
    if (!params) return text;

    return text.replace(/\{(\w+)\}/g, (match, key) => {
      return params[key] !== undefined ? String(params[key]) : match;
    });
  }
}
  1. 单例模式: 确保全局唯一的国际化实例,避免状态不一致
  2. 类型安全: 使用 TypeScript 定义清晰的类型接口
  3. 嵌套支持: LanguagePack 类型支持多层嵌套结构
  4. 延迟初始化: 只有在需要时才创建实例

3.语言包组织

export const zhCN: Record<string, any> = {
  // 通用模块
  common: {
    close: '关闭',
    confirm: '确认',
    cancel: '取消',
    // ...
  },

  // 功能模块 - 设置
  settings: {
    title: '渲染设置',
    renderSettings: '渲染设置',
    showBorder: '显示边框',
    showBorderTip: '显示模型构件的边线',
    // ...
  },

  // 更多模块...
};
export const enUS: Record<string, any> = {
  // Common
  common: {
    close: 'Close',
    confirm: 'Confirm',
    cancel: 'Cancel',
    // ...
  },

  // Settings
  settings: {
    title: 'Render Settings',
    renderSettings: 'Render Settings',
    showBorder: 'Show Border',
    showBorderTip: 'Display model component edges',
    // ...
  },
  
  // 更多模块...
};
  1. 按功能模块分组: 将相关的文本放在同一个命名空间下
  2. 层次化结构: 使用嵌套对象组织复杂的文本结构
  3. 一致性命名: 保持命名规范的一致性,如 xxxTip 表示提示文本
  4. 完整性保证: 确保所有语言包的结构完全一致

初始化调用

1.全局初始化

import { zhCN } from './languages/zh-CN';
import { enUS } from './languages/en-US';

// 创建全局实例
const i18n = I18nManager.getInstance({
  defaultLanguage: 'zh-CN'
});

// 注册语言包
i18n.setLanguagePack('zh-CN', zhCN);
i18n.setLanguagePack('en-US', enUS);

// 设置当前语言
i18n.setCurrentLanguage('en-US');

export { i18n };

2.实际使用示例

// 在业务代码中的使用
import { i18n } from '../../i18n/I18nManager';

export class TooltipManager {
  private getAnnotationTooltipMessages() {
    return {
      [AnnotationDwgType.CLOUD]: {
        initial: i18n.t('annotationDwg.initialTip'),
        drawing: i18n.t('annotationDwg.drawingTip'),
        mobileInitial: i18n.t('annotationDwg.mobileInitialTip') + i18n.t('annotationDwg.cloud'),
        mobileDrawing: i18n.t('annotationDwg.mobileDrawingTip') + i18n.t('annotationDwg.cloud')
      },
      // ...
    };
  }
}

扩展其他语言

后续如果要支持日语,只需提供日语对应的键值对,然后在manager中使用即可。需要注意的是,不同的语言长短不一样,可能对界面有所影响,css需要微调