@rockshin/i18nlint
v0.1.0
Published
i18n linting and management tools for extracting, cleaning, checking, and converting translation files
Maintainers
Readme
i18nlint
🚀 强大的 i18n 管理 CLI 工具 - 像使用 Prettier 一样管理你的国际化翻译!
自动提取翻译键、清理未使用的翻译、JSON 与 Excel 格式互转,让国际化管理变得简单高效。
English documentation: README.md
面向 Agent 的参考文档: llm.txt
📖 快速开始? 查看 快速入门指南
⚡ 性能升级! 已迁移到 SWC,性能提升 3 倍以上!查看 SWC 迁移报告
✨ 核心特性
🎯 CLI 优先设计
- 像 Prettier 一样,通过简单的命令行操作完成所有工作
- 完整的 TypeScript 类型支持,也可作为 Node.js 库使用
- 灵活的配置系统,支持多种配置文件格式
🔍 智能提取与管理
- 自动提取 - 扫描代码,自动提取所有翻译键,无需手动维护
- 智能清理 - 识别并移除未使用的翻译键,保持翻译文件整洁
- 运行时键保护 - 支持
runtimeKeys白名单,保护动态生成的翻译键不被清理 - 命名空间参数提取 - 支持从函数参数中提取命名空间(如
t('key', { ns: 'common' }))
📦 灵活的组织方式
- 多命名空间 - 支持按业务模块组织翻译
- 双文件模式 - 支持
locale-first和namespace-first两种文件组织模式 - 自定义翻译函数 - 支持配置自定义翻译库和函数来源
🤝 团队协作
- Excel 互转 - JSON ↔ Excel 双向转换,方便与翻译团队协作
- 统一输出目录 - 默认使用
.i18n-output目录管理 Excel 文件 - CI/CD 集成 - 支持在 CI 中检查翻译完整性
⚡ 极速性能
- 基于 SWC 解析器,比传统 Babel 方案快 3 倍以上
- 支持大型项目,高效处理数千个文件
📥 安装
# 使用 pnpm(推荐)
pnpm add -D @rockshin/i18nlint @swc/core xlsx glob
# 使用 npm
npm install --save-dev @rockshin/i18nlint @swc/core xlsx glob
# 使用 yarn
yarn add -D @rockshin/i18nlint @swc/core xlsx globPeer Dependencies
@swc/core^1.x - SWC 解析器xlsx^0.18.x - Excel 文件处理glob^11.x - 文件匹配
💡 从 Babel 迁移? 只需更新依赖即可,API 完全兼容!详见 SWC 迁移指南
🚀 快速开始
1. 创建配置文件
在项目根目录创建 i18nlint.config.ts:
import type { I18nLintConfig } from '@rockshin/i18nlint'
const config: I18nLintConfig = {
// 源代码目录
srcDir: './src',
// 翻译文件目录
localesDir: './src/locales',
// 支持的语言列表
languages: ['en-US', 'zh-CN', 'ja-JP'],
// 命名空间配置(会自动提取命名空间列表)
namespaceConfig: [
{
name: 'dashboard',
patterns: ['/dashboard/'],
// 可选:运行时动态键白名单(从后端获取的国际化 key)
runtimeKeys: ['dynamic.key.from.backend'],
},
{
name: 'common',
patterns: [], // 空数组表示默认命名空间
},
],
}
export default config2. 添加 npm 脚本
在 package.json 中添加:
{
"scripts": {
"i18n:extract": "i18nlint extractKeys",
"i18n:cleanup": "i18nlint cleanupUnusedKeys",
"i18n:to-excel": "i18nlint jsonToXlsx",
"i18n:from-excel": "i18nlint xlsxToJson",
"i18n:sort": "i18nlint sortTranslations"
}
}3. 开始使用
# 提取翻译键
pnpm i18n:extract
# 清理未使用的键
pnpm i18n:cleanup
# 导出到 Excel(输出到 .i18n-output 目录)
pnpm i18n:to-excel📋 CLI 命令
核心命令
| 命令 | 说明 | 常用选项 |
| --- | --- | --- |
| extractKeys | 从源代码提取翻译键 | --verbose, --throw-on-new-keys |
| cleanupUnusedKeys | 清理未使用的翻译键 | --output-dir, --verbose, --dry-run |
| check | 检查翻译完整性 | --check-extra-keys, --no-check-empty, --verbose |
| jsonToXlsx | 将 JSON 转换为 Excel | --output-dir, --namespace |
| xlsxToJson | 将 Excel 转换为 JSON | --input-file, --namespace |
| sortTranslations | 排序翻译文件 | - |
extractKeys 选项说明
--verbose- 显示详细的提取过程信息(默认: true)--throw-on-new-keys- 发现新键时抛出错误并阻止操作(用于 Git Hooks 和 CI/CD)- 返回错误码 1,可用于中断 Git 提交或 CI 流程
- 适合集成到 pre-commit hook 中,确保翻译完整性
命令示例
# 提取翻译键(详细模式)
i18nlint extractKeys --verbose
# 提取翻译键并在发现新键时报错(用于 Git Hooks)
i18nlint extractKeys --throw-on-new-keys
# 清理并输出到指定目录
i18nlint cleanupUnusedKeys --output-dir ./cleaned
# 预览清理结果,不写入文件
i18nlint cleanupUnusedKeys --dry-run
# 检查翻译完整性
i18nlint check
# 检查缺失、空值和多余键
i18nlint check --check-extra-keys
# 只导出特定命名空间(默认输出到 .i18n-output 目录)
i18nlint jsonToXlsx --namespace dashboard
# 从 Excel 导入(默认从 .i18n-output 目录读取)
i18nlint xlsxToJson --input-file ./.i18n-output/output-common.xlsx
# 排序翻译文件
i18nlint sortTranslations
# 显示帮助
i18nlint --help
# 显示版本
i18nlint --version⚙️ 配置
配置文件
工具会自动查找以下配置文件(按优先级):
i18nlint.config.tsi18nlint.config.jsi18nlint.config.mjsi18nlint.config.cjs.i18nlintrc.ts.i18nlintrc.js
也可以通过 --config 选项指定配置文件:
i18nlint extractKeys --config ./custom-config.ts配置选项
interface I18nLintConfig {
/** 源代码目录路径 */
srcDir: string
/** 本地化文件目录路径 */
localesDir: string
/** 支持的语言列表 */
languages: string[]
/** 命名空间配置 - 根据文件路径自动分配命名空间 */
namespaceConfig: NamespaceConfig[]
/** 是否清理未使用的 key(true=清理,false=只新增不清理,默认 false) */
removeUnusedKeys?: boolean
/** 本地化文件组织模式(默认: 'locale-first') */
localesPattern?: 'locale-first' | 'namespace-first'
/** 自定义翻译函数来源配置(默认: [{ source: 'useTranslation', property: 't' }]) */
translationFunctions?: TranslationFunctionSource[]
/** 是否从函数参数中提取命名空间(默认: false) */
extractNamespaceFromArgs?: boolean
/** 将 key 直接作为 value 的语言列表(默认: undefined,即只有 en-US 使用 key 作为 value)
* 例如: ['en-US', 'en-GB', 'en-AU']
* 这些语言的翻译文件中,新增的 key 会直接使用 key 本身作为 value
*/
keyAsValueForLocales?: string[]
/** 是否使用嵌套 JSON 对象格式(默认: false) */
nestedKeys?: boolean
}
interface NamespaceConfig {
/** 命名空间名称 */
name: string
/** 匹配的路径模式列表 */
patterns: string[]
/** 运行时动态键(从后端获取的国际化 key 白名单) */
runtimeKeys?: string[]
}💡 配置优化:从 v2.0 开始,不再需要单独配置
namespaces字段,命名空间列表会自动从namespaceConfig中提取。
补充说明:
keyAsValueForLocales未配置时,默认只有en-US会使用 key 本身作为 value。nestedKeys: true时,支持{ "a": { "b": "value" } }这种嵌套翻译文件结构。
Ignore 文件
创建 .i18nignore 文件来忽略特定文件或目录:
# 忽略测试文件
*.test.ts
*.spec.ts
__tests__
# 忽略构建输出
dist
build
node_modules推荐的 .gitignore 配置
建议将 .i18n-output 目录添加到 .gitignore:
# i18n 工具输出目录
.i18n-output/💡 使用场景
日常开发
# 1. 开发时添加新的翻译键
const title = t('dashboard.newFeature.title')
# 2. 提取翻译键
pnpm i18n:extract
# 3. 提交代码(翻译文件自动更新)
git add . && git commit -m "feat: add new feature"翻译协作工作流
# 1. 导出到 Excel(输出到 .i18n-output 目录)
pnpm i18n:to-excel
# 生成: .i18n-output/output-common.xlsx, .i18n-output/output-dashboard.xlsx
# 2. 发送给翻译团队进行翻译
# 3. 收到翻译后的 Excel 文件,放回 .i18n-output 目录
# 4. 导入翻译
pnpm i18n:from-excel
# 5. 提交更新
git add . && git commit -m "chore: update translations"定期维护
# 清理未使用的翻译键
pnpm i18n:cleanup
# 排序翻译文件
pnpm i18n:sort翻译完整性检查
# 检查缺失键和空翻译
i18nlint check
# 额外检查多余键
i18nlint check --check-extra-keyscheck 会检查:
- 代码里使用了但翻译文件里缺失的 key
- value 为空字符串的翻译
- 开启
--check-extra-keys时,检查翻译文件中未被代码使用的多余 key
Git Pre-commit Hook 集成
通过集成 Git Pre-commit Hook,可以在每次提交代码前自动检查 i18n 翻译键,防止未翻译的内容进入代码库。
快速开始 (使用 Husky)
# 1. 安装 husky
pnpm add -D husky
# 2. 初始化
npx husky init
# 3. 添加 pre-commit hook
npx husky add .husky/pre-commit "pnpm i18n:extract --throw-on-new-keys"
# 4. 配置 package.json
# 添加 "prepare": "husky install" 到 scripts工作流程
# 1. 添加新的翻译键
const title = t('new.feature.title')
# 2. 提交代码
git add .
git commit -m "feat: add new feature"
# ❌ 提交被阻止,提示有新键
# 3. 查看并提交翻译文件
git add .
git commit -m "feat: add new feature"
# ✅ 提交成功其他方案
- simple-git-hooks - 轻量级方案
- lint-staged - 性能优化方案
- 手动 Git Hooks - 完全自定义
📖 详细文档: 查看 Pre-commit Hook 集成指南 了解完整配置、多种方案对比、高级用法和故障排除。
CI/CD 集成
在 CI 中检查是否有未提取的翻译键:
# .github/workflows/ci.yml
- name: Check i18n keys
run: pnpm i18n:extract --throw-on-new-keys如果发现新键,构建会失败,提醒开发者先提取翻译。
💡 最佳实践: 同时配置 Pre-commit Hook 和 CI 检查,形成双重保障。
🔧 高级功能
1. 作为 Node.js 库使用
除了 CLI,也可以在代码中直接使用:
import { extractKeys, cleanupUnusedKeys, jsonToXlsx, xlsxToJson } from '@rockshin/i18nlint'
const config = {
srcDir: './src',
localesDir: './src/locales',
languages: ['en-US', 'zh-CN'],
namespaceConfig: [{ name: 'common', patterns: [] }],
}
// 提取翻译键
const stats = extractKeys(config, {
verbose: true,
throwOnNewKeys: false,
})
// 清理未使用的键
const cleanupStats = cleanupUnusedKeys(config, {
outputDir: './cleaned-locales',
verbose: true,
})
// JSON 转 Excel
const files = jsonToXlsx(config, {
outputDir: './.i18n-output',
namespace: 'common',
})
// Excel 转 JSON
xlsxToJson(config, {
inputFile: './.i18n-output/output-common.xlsx',
namespace: 'common',
})2. 自定义翻译函数来源
默认情况下,工具只识别 useTranslation() 的用法。如果你的项目使用自定义的翻译库,可以通过 translationFunctions 配置来支持。
支持的模式
模式 1:解构模式(Destructuring)
// 函数调用形式(如 React i18next)
import { useTranslation } from 'react-i18next'
const { t } = useTranslation()
t('hello.world')
// 直接引用形式(纯 TS 文件)
import { translationLib } from './lib'
const { t } = translationLib
t('custom.key')模式 2:直接赋值模式(Direct Assignment)
// 直接赋值给变量
import { i18n } from './i18n'
const t = i18n
t('copy success')
// 函数调用赋值
import { useTranslation } from 'react-i18next'
const translate = useTranslation()
translate('message')模式 3:对象方法调用(Member Expression)
// 标准对象方法调用
import { i18nInstance } from './i18n'
i18nInstance.t('copy success')
// 带参数的调用
i18nInstance.t('copy success', { ns: 'common' })配置示例
const config: I18nLintConfig = {
srcDir: './src',
localesDir: './src/locales',
languages: ['en-US', 'zh-CN'],
namespaceConfig: [{ name: 'common', patterns: [] }],
translationFunctions: [
// 解构模式(type 可省略)
{ source: 'useTranslation', property: 't' },
{ source: 'translationLib', property: 't' },
{ source: 'i18nService', property: 'translate' },
// 直接赋值模式(必须指定 type)
{ type: 'direct-assignment', source: 'i18n', variable: 't' },
{ type: 'direct-assignment', source: 'useTranslation', variable: 'translate' },
// 对象方法调用模式(必须指定 type)
{ type: 'member-expression', object: 'i18nInstance', method: 't' },
{ type: 'member-expression', object: 'i18n', method: 'translate' },
],
}📖 详细文档:查看 翻译函数配置文档
3. 命名空间参数提取
从 v0.2.1 开始,支持从翻译函数的参数中提取命名空间信息。
配置启用:
const config: I18nLintConfig = {
srcDir: './src',
localesDir: './src/locales',
languages: ['en-US', 'zh-CN'],
namespaceConfig: [
{ name: 'common', patterns: [] },
{ name: 'dashboard', patterns: ['/dashboard/'] },
{ name: 'errors', patterns: ['/errors/'] },
],
// 启用 ns 参数解析
extractNamespaceFromArgs: true,
}使用示例:
// 文件: src/dashboard/UserProfile.tsx
const { t } = useTranslation()
// 使用 common 命名空间(通过 ns 参数指定)
const saveButton = t('button.save', { ns: 'common' })
// ✅ 将收集到: common 命名空间
// 使用当前模块的命名空间(基于文件路径)
const profileTitle = t('profile.title')
// ✅ 将收集到: dashboard 命名空间
// 对象方法调用也支持
i18nInstance.t('error.notFound', { ns: 'errors' })
// ✅ 将收集到: errors 命名空间命名空间优先级:
- ns 参数(如果启用
extractNamespaceFromArgs且存在) - 文件路径(根据
namespaceConfig的patterns匹配)
📖 详细文档:查看 命名空间提取文档
4. 运行时动态键(Runtime Keys)
有些翻译键是在运行时动态生成的(例如从后端 API 获取),这些键在代码中不会直接出现,但需要保留在翻译文件中。使用 runtimeKeys 配置可以将这些键加入白名单:
namespaceConfig: [
{
name: 'common',
patterns: [],
runtimeKeys: [
// 从后端获取的错误消息键
'error.network',
'error.timeout',
'error.unauthorized',
// 从后端获取的状态键
'status.pending',
'status.approved',
'status.rejected',
],
},
]工作原理:
- 在执行
cleanupUnusedKeys时,runtimeKeys中的键会被视为"已使用" - 即使这些键在代码中没有直接引用,也不会被清理
- 适用于动态生成的翻译键、后端返回的国际化消息等场景
使用场景:
- 后端错误消息 - 后端返回错误码,前端根据错误码显示对应翻译
- 动态状态 - 工作流状态、订单状态等从后端获取
- 权限相关 - 权限名称、角色名称等动态内容
- 配置驱动 - 通过配置文件或 CMS 管理的翻译键
5. 嵌套 JSON 翻译结构
如果你的翻译文件是嵌套对象结构,可以开启 nestedKeys: true:
const config: I18nLintConfig = {
srcDir: './src',
localesDir: './src/locales',
languages: ['en-US', 'zh-CN'],
namespaceConfig: [{ name: 'common', patterns: [] }],
nestedKeys: true,
}示例:
{
"button": {
"save": "Save"
}
}6. 文件组织模式
i18nlint 支持两种文件组织模式,可通过 localesPattern 配置选择:
Locale-First 模式(默认)
按语言组织,每个语言目录下包含多个命名空间文件:
src/
locales/
en-US/
common.json
dashboard.json
zh-CN/
common.json
dashboard.json适用场景: 翻译人员按语言分工,与 i18next 等框架的默认结构一致
Namespace-First 模式
按命名空间组织,每个命名空间目录下包含多个语言文件:
src/
locales/
common/
en-US.json
zh-CN.json
dashboard/
en-US.json
zh-CN.json适用场景: 模块化开发,每个模块独立维护翻译
配置示例:
const config: I18nLintConfig = {
srcDir: './src',
localesDir: './src/locales',
languages: ['en-US', 'zh-CN', 'ja-JP'],
namespaceConfig: [
{ name: 'common', patterns: [] },
{ name: 'dashboard', patterns: ['/dashboard/'] },
],
// 选择文件组织模式
localesPattern: 'namespace-first', // 或 'locale-first'(默认)
}📖 详细文档:查看 Locales 文件组织模式文档
📝 翻译文件格式
JSON 文件格式
{
"welcome": "Welcome",
"login": "Login",
"logout": "Logout",
"settings": "Settings"
}Excel 文件格式
Excel 文件默认输出到项目根目录的 .i18n-output 目录,文件名格式为 output-{namespace}.xlsx。
文件位置示例:
.i18n-output/
output-common.xlsx
output-dashboard.xlsxExcel 表格结构:
| key | default | en-US | zh-CN | ja-JP | | --- | --- | --- | --- | --- | | welcome | Welcome | Welcome | 欢迎 | ようこそ | | login | Login | Login | 登录 | ログイン | | logout | Logout | Logout | 登出 | ログアウト | | settings | Settings | Settings | 设置 | 設定 |
📖 API 文档
核心函数
extractKeys
从源代码中提取翻译键并更新翻译文件。
function extractKeys(config: I18nLintConfig, options?: ExtractKeysOptions): ExtractStats[]cleanupUnusedKeys
扫描代码并清理未使用的翻译键。
function cleanupUnusedKeys(config: I18nLintConfig, options?: CleanupOptions): CleanupStats[]checkTranslations
检查翻译完整性,包括缺失、空值和可选的多余键检查。
function checkTranslations(config: I18nLintConfig, options?: CheckOptions): CheckResult[]jsonToXlsx
将 JSON 翻译文件转换为 Excel 格式。
function jsonToXlsx(config: I18nLintConfig, options?: JsonToXlsxOptions): string[]xlsxToJson
将 Excel 文件转换为 JSON 翻译文件。
function xlsxToJson(config: I18nLintConfig, options?: XlsxToJsonOptions): voidsortExistingTranslations
按字母顺序排序现有的翻译文件。
function sortExistingTranslations(config: I18nLintConfig): void工具函数
// 加载配置文件
function loadConfig(configPath?: string): Promise<I18nLintConfig>
// 加载 ignore 模式
function loadIgnorePatterns(cwd?: string): string[]
// 检查路径是否应该被忽略
function shouldIgnore(filePath: string, ignorePatterns: string[]): boolean
// 校验配置是否合法
function validateConfig(config: I18nLintConfig): void
// 创建默认配置
function createDefaultConfig(overrides?: Partial<I18nLintConfig>): I18nLintConfig
// 从配置中提取命名空间列表
function getNamespacesFromConfig(config: I18nLintConfig): string[]🎯 最佳实践
- 提交前检查 - 运行
pnpm i18n:extract确保所有翻译键都已提取 - 定期清理 - 每个迭代结束时运行
pnpm i18n:cleanup清理未使用的键 - 保持排序 - 使用
pnpm i18n:sort保持翻译文件整洁 - 版本控制 - 将配置文件和翻译文件都纳入版本控制,
.i18n-output目录建议添加到.gitignore - CI 集成 - 在 CI 中使用
--throw-on-new-keys确保翻译完整性 - 输出目录管理 - Excel 文件统一输出到
.i18n-output目录,避免污染项目根目录
🆕 更新日志
v0.2.4 - Pre-commit Hook 集成文档
新增文档:
- ✨ 新增完整的 Git Pre-commit Hook 集成指南
- 📖 提供 4 种集成方案(Husky/simple-git-hooks/lint-staged/手动)
- 🔧 包含高级配置、常见问题和故障排除
- 💡 更新 README,添加快速开始指南
- 📝 完善 CLI 命令说明,详细解释
--throw-on-new-keys用法
v0.2.3 - Key As Value 支持
新功能:
- ✨ 新增
keyAsValueForLocales配置选项 - 🌍 支持为多个语言配置使用 key 作为 value
- 🎯 灵活控制哪些语言使用 key 作为默认翻译值
- ✅ 完整的测试覆盖(4 个新测试用例)
配置示例:
const config: I18nLintConfig = {
srcDir: './src',
localesDir: './src/locales',
languages: ['en-US', 'en-GB', 'en-AU', 'zh-CN', 'ja-JP'],
namespaceConfig: [{ name: 'common', patterns: [] }],
// 配置多个英语变体使用 key 作为 value
keyAsValueForLocales: ['en-US', 'en-GB', 'en-AU'],
}行为说明:
- 如果配置了
keyAsValueForLocales,列表中的语言会使用 key 作为 value - 如果未配置,默认只有
en-US使用 key 作为 value(保持向后兼容) - 如果配置为空数组
[],则所有语言都使用空字符串
使用场景:
- 多英语变体项目 - 为 en-US、en-GB、en-AU 等英语变体都使用 key 作为默认值
- 灵活的默认值策略 - 根据项目需求自定义哪些语言使用 key 作为默认值
- 减少翻译工作量 - 对于相似的语言变体,可以先使用 key 作为默认值,后续再精细化翻译
v0.2.1 - 直接赋值模式支持
新功能:
- ✨ 新增直接赋值模式(Direct Assignment)支持
- 🎯 支持
const t = i18n这样的非解构赋值方式 - 🔧 新增
DirectAssignmentSource类型定义 - 📖 更新 翻译函数配置文档
- ✅ 完整的测试覆盖(9 个新测试用例)
配置示例:
translationFunctions: [
// 直接赋值模式
{ type: 'direct-assignment', source: 'i18n', variable: 't' },
{ type: 'direct-assignment', source: 'useTranslation', variable: 'translate' },
]v0.2.1 - 命名空间参数提取
新功能:
- ✨ 新增
extractNamespaceFromArgs配置选项 - 🎯 支持从翻译函数参数中提取
ns命名空间 - 🔄 命名空间优先级:参数 ns > 文件路径
- 📖 新增详细的 命名空间提取文档
- ✅ 完整的测试覆盖(11 个新测试用例)
v0.2.0- 文件组织模式支持
新功能:
- ✨ 新增
localesPattern配置选项,支持两种文件组织模式 - 📁 Locale-First 模式(默认):
locales/en-US/common.json - 📁 Namespace-First 模式:
locales/common/en-US.json - 🔧 所有功能(extract、cleanup、converters)完全支持两种模式
- 📖 新增详细的 文件组织模式文档
v0.2.0 - 配置优化
重大变更:
- ✨ 移除了
namespaces字段,命名空间列表现在自动从namespaceConfig提取 - ✨ 新增
getNamespacesFromConfig()工具函数 - 🔧 简化配置,避免重复维护命名空间列表
v0.1.0 - Runtime Keys 支持
- ✨ 新增
runtimeKeys配置,支持运行时动态键白名单 - 🔐 清理未使用键时自动保护
runtimeKeys中的键 - 📝 完善文档和示例
📄 导出内容
// 核心功能
export { checkTranslations } from './core/check-translations'
export { cleanupUnusedKeys, scanUsedKeys } from './core/cleanup-unused-keys'
export { jsonToXlsx, sortExistingTranslations, xlsxToJson } from './converters/converters'
export { extractKeys } from './core/extract-keys'
export { loadConfig, loadIgnorePatterns, shouldIgnore, validateConfig } from './config/config-loader'
// 类型定义
export type {
CheckOptions,
CheckResult,
CleanupOptions,
CleanupStats,
DestructuringSource,
DirectAssignmentSource,
ExtractKeysOptions,
ExtractStats,
I18nLintConfig,
JsonToXlsxOptions,
LocaleTranslations,
LocalesPattern,
MemberExpressionSource,
NamespaceConfig,
TranslationData,
TranslationFunctionSource,
XlsxToJsonOptions,
} from './types'
// 工具函数
export {
createDefaultConfig,
ensureDirectoryExists,
extractLanguagesFromConfig,
extractTextsFromTFunctions,
flattenTranslations,
getAllKeysFromLocale,
getNamespaceByPath,
getNamespacesFromConfig,
readTranslationFile,
sortObjectKeys,
unflattenTranslations,
writeTranslationFile,
} from './utils'📜 License
MIT
🤝 贡献
欢迎提交 Issue 和 Pull Request!
