国际化探索:提升开发体验与灵活性
date
Mar 17, 2025
slug
i18n-2
status
Published
tags
i18n
Technology
summary
最近公司平台需国际化改造,以适配不同语言环境。基于特定需求,展开了一系列技术探索与实践。
type
Post
在上一篇文章中,我们探讨了通过颗粒化方案实现平台的国际化改造。虽然该方案在减少语言包体积方面表现出色,但对开发者的友好度欠佳。
国际化探索:颗粒化方案本文将介绍一个另一种方案,着重提升开发体验,同时保持语言包体积的优化。
优化方案
该方案在第一版的基础上进行了优化,主要改进如下:
- 单语言加载:只加载当前语言所需的语言包,减少不必要的加载。
- namespace 拆分与路由拆分:根据不同的命名空间和路由,对语言包进行拆分,进一步减小单个语言包的体积。
- 提高开发体验:引入 TypeScript 智能提示,提升开发效率和体验。
i18n 实现
以下是
I18nService
的部分实现:看一下 I18nService 内部的实现,最基础的内容就是语言的检测,如果需要通过其他参数可以自行处理。
NS 拆分(路由拆分)
通过
loadNS
和 unloadNS
方法,可以动态加载不同路由下的语言包内容。同时也可以设置如
common
等通用语言包来进行全局使用,此功能的想法来此上一篇文章提及的 I18nProvider
。同时也只会加载当前检测到的语言对应的语言包,能够进一步节省其语言包使用空间。
单语言加载
但其实还有另一种实现,这里并没有使用,通过资源路径的方式取加载,这样更方便,只是是异步的。
我们可以将 i18n 文件统一放在
public
目录下,然后进行统一访问,就能实现如下功能:这样就会去找对应语言包目录下的 ns,实现更加精准的加载。
翻译函数
优化后的
t
函数支持参数传递、函数形式,并增强了 TypeScript 类型支持:看一下
t 函数
的内部的逻辑:- 转为数组
因为其参数既支持数组也支持字符串,所以统一转为数组进行处理。
- 找到每一个标识对应的翻译,并进行拼接。
- 翻译参数处理
t 函数支持匿名参数、命名参数以及默认值等内容,来动态的返回其翻译。
- 返回最终翻译
使用
通过案例,可以看到 t 函数的灵活,包括其提供的类型提示,很大程度上增强了开发体验。
缺失翻译处理
添加一个
handleMissingTranslation
来处理未命中的情况,来进行兜底处理。类型提示
通过
NestedTranslation
和 Path
类型,实现对翻译键的强校验和智能提示:语言包强校验
在一开始 i18n 实现 里面用到了如下的类型。
这里的用处是用于强校验不同语言包之间的 key 是否缺失。
如下所示:
t 函数路径提示
需要从两个方面来获取:
- 语言包声明上
回头看一下
I18nService
的声明:用到了
NestedTranslation
。它会解析所有的语言包得到所有的 key。- Path 拆解
t 函数
用到了 Path
这里的 K 是继承 T 的,而 T 就是所有语言包对象的 key 值,P 则是根据对应的 NS 得到最终是哪个语言包下面的 key 值来进行提示。
两者进行结合,最终得到了完整的类型提示:

使用
接下来,来从一个案例具体看看其用法。
语言包定义
首先我们定义一个通用的语言包,其 NS 为 common。
其目录结构如下:
语言包内容如下:
zh.ts
en.ts
这里可以注意到 英文语言包里的 翻译内容继承了一个类型。
这是翻译类型校验,以一个语言包为标准,确保其他语言包不会缺少字段进行校验。
其类型写在了
index.ts
文件中。index.ts
通过
loadNS
来加载语言包。到这里为止是一套 NS 实现的组合包。
如果是异步加载public/i18n
的情况,将其根据 lang 分别放置即可。这里不再做演示。ps: 如果使用这种方式的话,要注意应使用 js 或 json。
再来看一下 i18n 的入口。
这里注意一下
I18nService<{ common: CommonTranslation }>
,这里是实现类型提示的关键,和上面的翻译类型校验同理,获取其中的所有 key,为后续的 t 函数提示做铺垫。组件中使用
这里注意一个点,在路由切分或者其他拆分的情况下,记得将导入import './i18n'
放在顶部,以防止其导入的组件已经用到了其 NS。
第二版方案在开发体验上进行了显著优化,通过单语言加载、namespace 拆分和 TypeScript 智能提示,提高了开发效率和代码可维护性。同时,基于路由拆分和语言包拆分,分散其整体 i18n 体积。