首页 › tutorials › 造i18n提取Skill:扫中文秒出多语言包

造 i18n 提取 Skill:扫源码中文秒出多语言包

概述

本教程带你从零构建一个名为 i18n-extract 的自定义 Skill:它扫描项目源码里写死的中文文案,自动抽取成 zh-CN.json / en-US.json 多语言资源文件,并产出待人工确认的歧义清单。你将掌握 Skill 的目录结构、SKILL.md frontmatter 写法、渐进式披露(progressive disclosure)、捆绑脚本与 LLM 的分工模式,以及 ${CLAUDE_SKILL_DIR} 等字符串替换机制。适合会写命令行、做过前端/全栈项目、想让 Claude 替自己处理重复性工程任务的开发者。

前置条件


一、先理解 Skill 的本质:渐进式披露

很多人把 Skill 等同于"一段提示词",这只对了一半。Skill 的真正工程价值在于渐进式披露(progressive disclosure)——让 Claude 在大部分时间里只花极少 token,在真正需要时才加载完整能力。

它分三层运作:

层级 加载时机 进入上下文的内容 Token 成本
元数据层 会话启动 仅 name + description(描述被截断在 1,536 字符内) 极低
指令层 被 /调用 或模型判定相关时 完整 SKILL.md 正文 中
参考层 正文里显式 Read 时 reference/*.md、模板等 按需

官方文档把这个机制说得很直白:"一旦某个 Skill 被加载,它的内容会在后续轮次里持续留在上下文中,所以每一行都是反复发生的 token 成本。" 这就是为什么我们要把"机械扫描"这种脏活交给脚本,把"键名命名 + 翻译"这种需要判断的活留给模型——脚本只是被执行、不进上下文,而 SKILL.md 正文要尽量精简。

理解这一点后,我们设计 i18n-extract 的分工:

这种分工是本 Skill 的灵魂,也是判断一个 Skill 设计是否优秀的试金石。


二、创建 Skill 目录结构

Skill 放在哪里,决定了它的作用域。官方约定:

我们做一个个人级 Skill,目录结构如下:

~/.claude/skills/i18n-extract/
├── SKILL.md                      # 入口,必须存在
├── scripts/
│   └── scan_zh.js                # 捆绑脚本:机械扫描中文
├── reference/
│   └── key-conventions.md        # 参考文件:键名命名规范(按需加载)
└── templates/
    └── locale.json               # 输出模板

注意:SKILL.md 是唯一必须的文件,其余都是可选的"支撑文件"。官方建议把 SKILL.md 控制在 500 行以内,大段参考材料拆到独立文件、并在正文里用 Markdown 链接引用,好让模型知道"什么时候该去读它"。


三、写 SKILL.md:frontmatter 与正文

SKILL.md 由两段组成:顶部的 YAML frontmatter(配置行为)和下面的 Markdown 正文(给模型的指令)。

完整文件

---
name: i18n-extract
description: 扫描源码中的硬编码中文文案,抽取为 i18n 多语言资源文件(zh-CN.json / en-US.json)并生成歧义清单。当用户提到"国际化 / i18n / 多语言 / 文案抽取 / 把中文抽出来 / 抽成多语言包"时使用。
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/scan_zh.js *) Read Write Edit
argument-hint: [扫描目录] [目标语言...]
---

# i18n 文案提取

把源码里写死的中文,抽取成可维护的多语言资源。
**机械扫描交给脚本,键名命名与翻译交给模型。**

## 工作流

1. 运行捆绑扫描脚本,拿到全部中文文案(脚本输出 JSON 数组):

   !\`${CLAUDE_SKILL_DIR}/scripts/scan_zh.js "$ARGUMENTS"

   输出形如:`[{ "file": "src/Home.jsx", "line": 12, "text": "欢迎回来" }, ...]`

2. 对每条 `text` 去重;遇到同文不同义(按钮文案 vs 标题),拆成不同键。

3. 按 reference/key-conventions.md 的约定生成语义化键名(如 `home.welcome.title`)。

4. 写入 `locales/zh-CN.json`:键名 → 原始中文(**保持原文不翻译**)。

5. 写入 `locales/<目标语言>.json`(默认 `en-US.json`):键名 → 对应译文。
   占位符 `{name}` / `%s` / `{0}` 必须**原样保留**。

6. 输出报告:抽取总条数、去重后键数、以及"上下文不足、需人工确认"的歧义文案清单。

## 铁律
- **不要**自动把源码里的文案改写成 `t('key')`,除非用户明确要求。改写有风险,先让用户 review 资源文件。
- 翻译时保持简洁、贴近 UI 语境;按钮用动词,标题用名词短语。
- 键名全小写、点分层级、不超过 4 层。

## 附加资源
- 完整键名命名规范见 [reference/key-conventions.md](reference/key-conventions.md)

frontmatter 字段逐条解释

正文里的两个关键机制

第一是 !<命令>` 动态注入。这种语法会在 Skill 内容发送给模型之前先跑命令,把输出原地替换掉占位符。于是模型拿到的不是"请运行 scan_zh.js",而是脚本已经执行完的真实 JSON 结果。这是 Skill 区别于普通提示词的杀手锏——把"现场取数"固化进工作流。

第二是 $ARGUMENTS 字符串替换。/i18n-extract src en-US ja-JP 执行时,$ARGUMENTS 会被替换成 src en-US ja-JP,整段传给脚本和模型。argument-hint 里的 [目标语言...] 提示用户可以一次指定多个语言。

常见问题:Skill 没被自动触发? 90% 是 description 写得不到位。把"何时用"的触发词写具体,别只写"处理国际化"。


四、写扫描脚本 scan_zh.js

这是机械活,不该让模型一边猜一边扫。脚本职责单一:遍历目录、找出含中文的字符串字面量、输出结构化 JSON。

#!/usr/bin/env node
// scripts/scan_zh.js
// 扫描目录下源码文件中的中文字符串字面量,输出 JSON 数组。
// 用法: node scan_zh.js <目录> [--ext=js,jsx,ts,tsx,vue]
const fs = require('fs');
const path = require('path');

const root = process.argv[2] || '.';
const extArg = process.argv.find(a => a.startsWith('--ext='));
const exts = (extArg ? extArg.split('=')[1] : 'js,jsx,ts,tsx,vue').split(',');
const SKIP = ['node_modules', '.git', 'dist', 'build', '.next', 'locales'];

// 匹配含至少一个中文字符的字符串字面量(单引号/双引号/模板串)
const ZH_IN_STR = /([\u4e00-\u9fa5])/;

const results = [];

function walk(dir) {
  let entries;
  try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
  catch { return; }
  for (const e of entries) {
    if (SKIP.includes(e.name)) continue;
    const full = path.join(dir, e.name);
    if (e.isDirectory()) walk(full);
    else if (exts.some(x => e.name.endsWith('.' + x))) scanFile(full);
  }
}

function scanFile(file) {
  const lines = fs.readFileSync(file, 'utf8').split('\n');
  lines.forEach((line, i) => {
    // 每行新建一个带 g 标志的正则,避免 lastIndex 跨行污染
    const re = /(['"`])([^'"`\n]*[\u4e00-\u9fa5][^'"`\n]*)\1/g;
    let m;
    while ((m = re.exec(line))) {
      results.push({
        file: path.relative(root, file),
        line: i + 1,
        text: m[2].trim()
      });
    }
  });
}

walk(root);
console.log(JSON.stringify(results, null, 2));

预期输出

在测试项目根目录跑 node ~/.claude/skills/i18n-extract/scripts/scan_zh.js src,你会得到:

[
  { "file": "src/App.jsx", "line": 8,  "text": "个人中心" },
  { "file": "src/Home.jsx", "line": 12, "text": "欢迎回来,{name}" },
  { "file": "src/Home.jsx", "line": 31, "text": "退出登录" }
]

这份 JSON 就是模型后续所有工作的唯一数据源——去重、命名、翻译全部基于它进行。

常见问题与解决


五、写参考文件与模板

把会膨胀的内容挪出 SKILL.md,是控制 token 成本的核心动作。

reference/key-conventions.md(按需加载,正文里用链接引用,模型只在需要命名规则时才 Read 它):

# i18n 键名命名约定

- 结构:`模块.区域.用途`,例如 `user.profile.editTitle`
- 段落用全小写驼峰(lowerCamelCase),用 `.` 分层,最多 4 层
- 后缀语义化:
  - 按钮用 `.btn`(`auth.login.btn`)
  - 标题用 `.title`(`home.welcome.title`)
  - 提示/说明用 `.tip`(`form.email.tip`)
  - 校验错误用 `.err`(`form.email.err`)
- 含变量的键,值里用 `{var}` 占位,例如 `"home.welcome.title": "欢迎回来,{name}"`
- 同一文案出现在多个模块,各自建立独立键,避免跨模块耦合

templates/locale.json(给模型看输出长什么样):

{
  "scope.section.key": "译文(保留 {placeholder} 占位符)"
}

这两份文件分别示范了 Skill 的两类支撑文件:reference(被读取的知识)与 template(被填写的格式)。SKILL.md 保持精简、只负责导航,是它们能各司其职的前提。


六、测试、调用与调试

Skill 在 ~/.claude/skills/ 下增删改会话内即时生效,无需重启(仅顶层全新目录才需重启一次)。测试分三步:

1. 直接验证脚本(先确保机械层正确):

node ~/.claude/skills/i18n-extract/scripts/scan_zh.js /path/to/project/src

2. 验证 Skill 被识别:在 Claude Code 里输入 /,应能在列表看到 i18n-extract,参数提示显示 [扫描目录] [目标语言...]。

3. 端到端调用:

/i18n-extract src en-US ja-JP

模型会:先跑 scan_zh.js src 拿到 JSON → 去重并按约定命名 → 写出 locales/zh-CN.json、locales/en-US.json、locales/ja-JP.json → 产出报告。

常见问题:


七、实战案例:把一个 React 项目抽成三语言包

假设项目 my-app/src 里有这些写死的中文:

// src/App.jsx
<h1>个人中心</h1>
<button>退出登录</button>

// src/Home.jsx
<p>欢迎回来,{name}</p>
<span>当前余额:{balance} 元</span>

执行 /i18n-extract src en-US ja-JP,模型产出:

locales/zh-CN.json:

{
  "account.title": "个人中心",
  "account.logout.btn": "退出登录",
  "home.welcome.title": "欢迎回来,{name}",
  "home.balance.tip": "当前余额:{balance} 元"
}

locales/en-US.json:

{
  "account.title": "My Account",
  "account.logout.btn": "Sign Out",
  "home.welcome.title": "Welcome back, {name}",
  "home.balance.tip": "Current balance: {balance}"
}

locales/ja-JP.json:

{
  "account.title": "マイアカウント",
  "account.logout.btn": "ログアウト",
  "home.welcome.title": "おかえりなさい、{name}",
  "home.balance.tip": "現在の残高:{balance}"
}

报告(模型在对话里输出):

扫描:2 个文件,抽取 4 条中文文案
去重后键数:4(无重复)
语言包:zh-CN.json / en-US.json / ja-JP.json 已写入 locales/
歧义待确认:0
提示:源码尚未改写为 t('key'),review 资源文件后可用
     /i18n-extract-replace 一键替换(需你授权)。

{name}、{balance} 占位符在三种语言里都被原样保留——这是 SKILL.md 正文铁律的功劳,证明指令确实在持续约束模型行为。整个流程从一条命令到三份语言包,耗时以秒计。


工作流程图

flowchart TD
    A["用户输入<br/>/i18n-extract src en-US ja-JP"] --> B{"会话启动时<br/>仅加载 description"}
    B -->|"$ARGUMENTS 替换 + 脚本预执行"| C["scan_zh.js 机械扫描<br/>输出中文文案 JSON"]
    C --> D["模型读取 JSON<br/>去重 + 判断歧义"]
    D --> E{"需要命名规范?"}
    E -->|是| F["按需 Read<br/>reference/key-conventions.md"]
    E -->|否| G["生成语义化键名"]
    F --> G
    G --> H["写 zh-CN.json<br/>键名→原文"]
    G --> I["写 en-US / ja-JP.json<br/>键名→译文"]
    H --> J["输出报告<br/>条数/键数/歧义清单"]
    I --> J
    J --> K["用户 review 后<br/>决定是否改写源码"]

    style C fill:#e8f5e9
    style H fill:#fff3e0
    style I fill:#fff3e0
    style F fill:#e3f2fd

图中绿色节点是"被执行、不进上下文"的脚本层,橙色是产物,蓝色是按需加载的参考层——三层分工一目了然,正是渐进式披露的工程兑现。


进阶:还能怎么强化这个 Skill


延伸阅读


训练营适用性评估


参考来源

  1. Extend Claude with skills — Claude Code Docs — 官方 Skill 开发文档,frontmatter 字段、字符串替换、支撑文件与渐进式披露的权威来源。
  2. skill-development/SKILL.md — anthropics/claude-code (GitHub) — 官方"如何写好 Skill"的元 Skill,强触发描述与祈使句写作规范。
  3. Claude Code Skills 2026 — Progressive Disclosure — AnhTu.dev — 三层渐进式披露的工程团队深度解读。
  4. Claude Agent Skills Framework: Build Specialized AI Agents — DigitalApplied — SKILL.md 结构与跨端(Claude.ai / Code / API)框架指南。
  5. Claude Code Skills: The Complete Guide (2026) — DesignRevision — 从零到生产的 Skill 完整实战指南。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入