i18n-key-usage
查找TypeScript项目中未使用、丢失和未解析的i18n消息键。
大多数键检查器对每个文件执行grep t("…") 调用。它忽略了常量中存储的键
通过帮助程序,键入为键联合或从枚举构建,因此它们将许多使用的键报告为
未使用的。 i18n-key-usage 计算键在代码中的流动方式:
- 在模块: 进口、再出口和
export *,加上tsconfig路径别名。 - 通过价值观: 常量、对象、映射、数组、扩展、三元格式、模板字面值、
解构,函数参数和返回值;
Array.map回调和React状态。 - 通过类型: 键入为键类型的值(例如:
TMessageKey)作为关键引用 无论在哪里使用。 - 严格的动态键: 报告一个它不能解析为有限集合的键,它的
位置,而不是在使用时对整个命名空间进行静默计数。家庭如
status.*针对列出其值的枚举声明,过时的声明将失败。
解析使用 光交叉连接. 一个3200个文件的Next.js应用程序包含5400个键 25秒。
支持库: next-intl. 分析仪的核心是 library-agnostic;欢迎其他库的适配器(参见 适配器).
快速启动
npm install --save-dev i18n-key-usage
创建 i18n-key-usage.config.json 在项目根目录下:
{
"$schema": "./node_modules/i18n-key-usage/schema.json",
"messages": "messages/en.json"
}
运行该程序:
npx i18n-key-usage
Unused message: home.subtitle (messages/en.json)
src/app/page.tsx:12: Unknown message key: home.titel
t("titel")
Scanned 214 files: 380/381 keys referenced, 1 unused, 1 missing, 0 unresolved.
退出代码: 0 干净, 1 发现的问题, 2 配置或解析错误。
GitHub行动
name: i18n
on: pull_request
jobs:
keys:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 24
- uses: saasykits/i18n-key-usage@v0
with:
working-directory: . # project root, e.g. apps/web in a monorepo
结果以注释的形式出现在拉取请求和工作摘要中的表格中。动作运行
与其标签(@v0 运行最新的0。X),因此不需要安装步骤。集
version 确定一个确切的释放。
每一个 配置 在烤肉串的情况下,选项也是一个输入,因此项目可以跳过 配置文件或覆盖每个工作流。输入将替换配置文件的值;列表一次 项目每行或逗号分隔的项目:
- uses: saasykits/i18n-key-usage@v0
with:
messages: messages/en
include: |
src/**/*.{ts,tsx}
key-types: src/i18n/types.ts#TMessageKey
families: |
orders.status.{{EOrderStatus}}
它报告什么
| 问题 | 意义 |
|---|---|
| 未使用的 | 没有应用程序代码引用的目录键。 |
| 失踪 | 代码引用源编目未定义的键。 |
| 未解决的 | 不能确定其可能值的动态键。 t(row.labelKey) 在 labelKey 是一个任意字符串。键入它作为键类型,缩小它,或声明一个族。 |
| 无效的声明 | 残缺的、未使用的或陈旧的家庭或保留的钥匙。 |
测试、fixture、生成的文件和目录导入(例如通过 messages (提供者)不
算作使用量。
配置
所有路径都是相对于项目根目录的。只有 messages 是必需的,配置文件本身呢
当选项来自标志或环境变量时是可选的。完整的模式作为
schema.json。
| 选项 | 默认的 | 描述 |
|---|---|---|
messages | 源语言环境目录:一个JSON文件,或者一个目录,每个顶级名称空间有一个JSON文件(messages/en/common.json 持有 common.*). | |
library | "next-intl" | 哪个适配器可以识别翻译器。 |
include | ["**/*.{ts,tsx}"] | 要扫描的源。 |
exclude | 测试,设备, *.d.ts, node_modules,构建输出 | 从 include。 |
catalogs | ["messages/*"] | 编目文件或目录。导入一个是目录读取,而不是使用。 |
tsconfig | "tsconfig.json" | 其中导入别名(compilerOptions.paths)来自。 |
keyTypes | [] | 值为消息键的类型,例如: { "file": "src/i18n/types.ts", "export": "TMessageKey" }。 |
translatorTypes | [] | 值为翻译器的类型,例如an ITranslator 带有呼叫签名的接口。 |
messageLoaders | [] | 返回整个目录的函数。结果上的成员读取计数为使用率。 |
keyFunctions | [] | 函数的参数为 argument (从零开始)是一个键,例如: translateMessage(t, key)。 |
keyProperties | [] | 字符串值为键的对象属性。 ["labelKey", "titleKey"]。 |
validationLibraries | [] | 调用参数可以是键的模块。 ["zod"] 为 z.string().min(1, "errors.required")。 |
literalKeyPrefixes | [] | 任何带有这些前缀之一的字符串字面值都是键。 ["errors."] 对于在服务器上抛出的错误代码。 |
families | [] | 动态密钥族,如 "orders.status.{{EOrderStatus}}",见下文。 |
retained | [] | 要保留的键,尽管没有引用它们,例如用于历史数据。 |
标志和环境变量
每个选项都有三种设置方式。一个标志取代了 环境变量,用来替换配置文件:
| 配置文件 | 国旗 | 环境变量 |
|---|---|---|
messages | --messages messages/en | I18N_KEY_USAGE_MESSAGES |
include | --include 'src/**/*.{ts,tsx}' | I18N_KEY_USAGE_INCLUDE |
catalogs | --catalog 'messages/*' | I18N_KEY_USAGE_CATALOGS |
keyTypes | --key-type src/i18n/types.ts#TMessageKey | I18N_KEY_USAGE_KEY_TYPES |
keyFunctions | --key-function src/i18n/t.ts#translate:1 | I18N_KEY_USAGE_KEY_FUNCTIONS |
families | --family 'orders.status.{{EOrderStatus}}' | I18N_KEY_USAGE_FAMILIES |
retained | --retain legacy.title | I18N_KEY_USAGE_RETAINED |
其余的遵循相同的方案(--exclude, --key-property, --literal-key-prefix,……);
i18n-key-usage --help 把它们都列出来。列表标记重复,并且每个列表也接受
逗号分隔或换行分隔的值;括号内的逗号,例如 {ts,tsx} 保存。
符号引用被写入 file#export、关键功能 file#export:argument。
--config, --root 和 --format 读 I18N_KEY_USAGE_CONFIG, I18N_KEY_USAGE_ROOT 和
I18N_KEY_USAGE_FORMAT。
动态键族
当在运行时从有限的值集构建键时,请使用占位符声明该族 命名列出值的导出:
{
"families": ["orders.status.{{EOrderStatus}}", "orders.{{REGIONS}}.{{EOrderStatus}}"]
}
占位符为导出的枚举、常量对象、常量数组或字符串字面值联合类型命名
在扫描的源。如果有多个文件导出名称,写入 {{src/orders/status.ts#EOrderStatus}}。
几个占位符扩展到每个组合。只有那些键被使用,所以一个被删除的enum
成员使其键未使用,并且没有代码再匹配的族被报告为过时。
只有当分析器无法看到值时才需要family。模板,例如
t(`status.${status}`) 自行解决 status 类型为enum还是字符串的并集
字面值。
尽可能选择键入的键而不是家庭键。类型化为您的 keyTypes 在
每一个生产它的地方,不需要声明:
import type { TMessageKey } from "@/i18n/types";
export const STATUS_LABELS: Record<EOrderStatus, TMessageKey> = {
[EOrderStatus.Open]: "orders.status.open",
[EOrderStatus.Closed]: "orders.status.closed",
};
编程API
import { checkMessageUsage, createReport, loadConfig } from "i18n-key-usage";
const { options, keyFiles } = loadConfig(process.cwd());
const report = createReport(checkMessageUsage(options), keyFiles);
checkMessageUsage 也在内存中接受 sources,这就是测试套件运行的方式。
适配器
适配器告诉分析器哪些导入创建了翻译器和目录
(src/adapters/next-intl.ts):
export const NEXT_INTL_ADAPTER: II18nAdapter = {
name: "next-intl",
factories: { "next-intl": FACTORIES, "next-intl/server": FACTORIES },
namespaceOption: "namespace",
keyMethods: ["rich", "markup"],
rawMethods: ["raw"],
probeMethods: ["has"],
};
具有相同模型的库(工厂返回作用域为点分隔名称空间的转换器)
通过添加适配器并将其注册来匹配 src/adapters/index.ts。
具有其他键语法的库,例如i18next的 ns:key 或 keyPrefix,需要小芯
的变化。欢迎发布和pull请求。
发展
pnpm install
pnpm test
pnpm build
pnpm check lints和格式与Biome, pnpm typecheck 运行TypeScript和 pnpm schema
再生 schema.json 配置更改后。