mcpField
F
find-unwrapped-strings

find-unwrapped-strings

github.com/lingui测试与质量10138 次安装1 周前更新
代码仓库

关于 find-unwrapped-strings

find-unwrapped-strings 是一项用于检查 Lingui 项目中未加本地化包装的用户可见字符串的技能。它规定了使用 ESLint 扫描、逐项判断、分批修复及报告结果的流程。

主要功能

  1. 01使用 Lingui 规则运行 ESLint 扫描
  2. 02按字符串用途审核扫描结果
  3. 03提供使用宏包装字符串的指引
  4. 04限制修复轮次并重新扫描
  5. 05记录处理结论及遗留项

适用场景

  1. 01Lingui 迁移后检查本地化覆盖
  2. 02发现仅检查目录时遗漏的字符串
  3. 03审核数据与配置中的字符串
  4. 04跟踪尚未解决的审计结果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lingui/skills --skill find-unwrapped-strings

安装后在 Claude Code 中输入/find-unwrapped-strings确认已加载。

附带文件references/eslint-config.md

详细介绍

查找未封装的字符串

何时运行

  • 完成 Lingui 设置或迁移到 Lingui 后——两者都会封装自己处理的内容,但都不能证明代码库的其余部分也已处理。
  • 当报告显示“构建通过、目录也已填满,但这个屏幕在所有语言环境中仍显示英文”时。
  • 在已建立 i18n 的项目中,定期进行覆盖率审计。

漏掉的字符串有规律可循,并非随机出现。JSX 中的字符串会被封装,因为它们看起来像 UI;剩下的则是不像 UI 的显示文案——例如数据模块中的内容(export const products = [{ name: "AeroPress Go" }])、toast 和错误辅助函数中的消息映射,以及配置对象中的标签。

扫描器有意设计得宁可多报。 精准度来自逐条依据跳过列表判断命中项,而不是不断调整扫描器,直到它不再报告结果。安静的扫描器和干净的代码库无法区分,而只有后者值得拥有。

判断时看字符串的作用,而不是所用语言。扫描器会根据结构标记字符串字面量,跳过列表则决定哪些字符串面向用户——字符串看起来像是用哪种自然语言写成的,并非判断依据,弄清这一点纯属额外开销。

步骤 1 — 确保防护措施就位

安装 eslint-plugin-lingui,并按照 references/eslint-config.md 中调优后的选项,将 no-unlocalized-strings 启用为 warn:

npm install --save-dev eslint-plugin-lingui

使用当前发布版本即可——它只是一个 lint 开发依赖,无需用固定版本范围来提供保护。

这项安装是永久性的。 审计只是一次性扫描;该规则才能阻止下一次回归。审计找出 30 个字符串,却没有留下任何防护措施,只能管用几周。

请显式启用该规则。插件的 recommended 和 flat/recommended 预设均不包含此规则,因此扩展预设并不代表获得了覆盖——详情见参考文档。

如果用户拒绝新增开发依赖,还有一次性模式——在仓库外编写调优后的配置,并让 ESLint 使用它,不改动项目:

npx eslint -c /tmp/lingui-audit.config.js --no-config-lookup 'src/**/*.{ts,tsx}' --format json

只有在用户拒绝后才提供此方案,并说明代价:审计可以正常进行,但没有任何措施能阻止下一个未封装的字符串,下一次审计也得从零开始。建议安装依赖。

无论采用哪种方式,只要扫描成功运行且规则报告了结果,此步骤就完成了。

步骤 2 — 扫描

npx eslint 'src/**/*.{ts,tsx}' --format json > /tmp/lingui-scan.json

筛选出 ruleId === "lingui/no-unlocalized-strings" 的结果,并精简为 file:line 加上违规源代码行——参考文档中提供了 jq 命令。这个列表就是待处理清单;从上到下逐项处理。

将规则选项保留在配置文件中,运行 CLI 时不要传入规则标志。带选项的 --rule 会替换已配置的选项,而不是与其合并,导致命中列表大约翻倍,增加类名和日志参数——更长的列表看起来似乎更全面,于是其中的垃圾结果就会通过封装标识符来“修复”。测量数据见参考文档。

如果扫描结果干净,但你有理由怀疑代码库存在问题,请先检查 glob,再相信结果:files 模式 src/**/*.ts 永远不会 lint .tsx,但运行仍会以 0 退出。

步骤 3 — 判断每个命中项

每个命中项都必须有一个处置结果——已包装,或已跳过并记录原因。没有任何命中项可以在未确定处置结果的情况下离开此步骤。

跳过列表位于 lingui-best-practices 技能的“Don't Wrap Non-UI Strings”一节——它是决定哪些内容应保持未包装状态的权威依据。确定处置结果前,请先阅读该节。如果无法查阅,则依据下方的角色测试进行判断。

处置结果取决于字符串的角色,而非它的形式。在数据模块中的 "Best seller" 是购物者会看到的徽标文字——应包装。同一对象中的 "aero-press-go" 是 URL 路径段——应跳过。以下两个问题可以判定有歧义的情况:

  • 翻译人员能否在不造成任何问题的情况下更改它?(作为状态值的 "DRAFT":不能。)
  • 如果这段内容以日语显示,结果是正确的还是错误?(sku 显示成日语就是错误。)

如果仅凭当前代码行无法判断,就查看使用它的地方。字符串的角色由它的使用位置决定,而不是定义位置。

步骤 4 — 分批修复,控制范围

按照 lingui-best-practices 中的宏决策树进行包装。本次审查中最常见的情况:

  • JSX 内容 → Trans
  • 组件内的字符串(属性、alert、函数参数)→ useLingui() + t`…`
  • 组件外的字符串 — 数据模块、模块级映射、配置对象 → 使用来自 @lingui/core/macro 的 msg 描述符,并在使用该字符串的位置解析。

最后一种情况正是本次审查要处理的内容,也是最容易修错的情况。模块作用域中的 t 会在导入时,根据当时恰好处于启用状态的区域设置解析一次,此后便不会改变。用 msg 定义,并在渲染时解析:

// src/data/products.ts — definition
import { msg } from "@lingui/core/macro";

export const products = [
  { sku: "sku-1001", slug: "aero-press-go", name: msg`AeroPress Go` },
];
// consuming component — resolution
const { t } = useLingui();
<h2>{t(products[0].name)}</h2>;

字段类型会从 string 变为 MessageDescriptor,因此 TypeScript 会指出所有使用位置——逐一处理。在没有渲染时机的非 React 代码中,i18n._(descriptor) 会在调用时解析,这正是 toast 辅助函数所需的行为。

包装时,按照 enhanced-message-context 添加翻译人员注释:数据模块标签和单词徽标是必须添加注释的情况,因为它们没有周边 UI 可供推断含义。

然后,每轮执行一次:

npx lingui extract --clean
npx lingui compile

并从步骤 2 重新扫描。

范围限制:最多 2 轮修复,或约 40 个文件。 达到任一上限后,停止并报告。人工可以重新运行审查,但无法审查 200 个文件的差异。

步骤 5 — 结束

满足以下任一条件时停止,并说明是哪一种:

  1. 扫描结果干净。
  2. 某轮没有包装任何新字符串 — 所有剩余命中项都已确认是误报。根据参考文档规定的规范,将这些具体情况加入规则配置的忽略列表,让防护规则保持安静,同时继续报告其他所有内容。严重级别保持为 warn,规则继续启用,命中项应使用忽略项,而不是 eslint-disable 注释。
  3. 已达到上限(2 轮 / 约 40 个文件)。

步骤 6 — 报告

每次扫描中的每个命中项都应归入以下三个类别之一,并注明 file:line:

  • 已包装 — 说明更改内容以及使用的宏。
  • 已跳过 — 注明跳过列表中的原因(slug、enum value、log output)。
  • 剩余项 — 仍未处理的命中项,无论是否已审查都要逐项列出,以便下次从这里开始。

同时报告防护规则的状态:严重级别,以及你添加的忽略项和每项对应的依据。

在宣称审查已完成之前,请说明每个命中项的处理结果。“我包装了 16 个字符串”不算结果;“包装了 16 个,将 16 个作为标识符和日志输出跳过,剩余 0 个,规则保持为 warn”才算。未报告的剩余项正是这项技能要防止的唯一结果——这些遗漏最初之所以看不见,恰恰是因为没人记录它们。

由其他工作流调用?

编排器(设置技能、TMS 连接技能)可能会提供范围(仅限 src/features/checkout,或迁移涉及的文件)、来自项目规则文件的额外跳过列表条目(品牌名称、产品代码),或已知遗漏(“前一轮检查遗漏了数据层”)。

请遵循这三项要求。流水线和报告约定保持不变:范围会缩小扫描内容,但不会改变报告内容。范围之外的发现应报告为范围外,而不是省略。

相关技能

  • lingui-best-practices — 负责维护跳过列表,以及步骤 3 和步骤 4 所用的宏决策树。
  • enhanced-message-context — 为这项审查发现的简短、缺乏上下文的标签提供翻译者注释。

评论

支持 Markdown