国际化(Internationalization,简称 i18n) 是指设计、开发产品或服务(尤其是软件、网站、内容、业务流程等)时,使其能够轻松适应不同语言、地区和文化环境的过程和技术。它的核心目标是消除地域和文化障碍,为全球用户提供无障碍的体验。
为什么叫 i18n?
这是一个常见的缩写,源自单词 “Internationalization” 的首字母 I 和末字母 n,中间有 18 个字母,因此简写为 i18n。
实现原理
- 使用键值对存储不同语言的翻译包
- 用一个管理器使用对应的语言进行翻译
- 代码中原本写死的中文,用函数替换,参数是语言包对应的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;
});
}
}
- 单例模式: 确保全局唯一的国际化实例,避免状态不一致
- 类型安全: 使用 TypeScript 定义清晰的类型接口
- 嵌套支持:
LanguagePack类型支持多层嵌套结构 - 延迟初始化: 只有在需要时才创建实例
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',
// ...
},
// 更多模块...
};
- 按功能模块分组: 将相关的文本放在同一个命名空间下
- 层次化结构: 使用嵌套对象组织复杂的文本结构
- 一致性命名: 保持命名规范的一致性,如
xxxTip表示提示文本 - 完整性保证: 确保所有语言包的结构完全一致
初始化调用
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需要微调