Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ fi

rm -f "$lint_files"

# 涉及翻译 json 文件时,强制跑机械完整性检查(缺失/多余 key、缺失 messages.json 等),
# 未通过则拒绝提交。--diff-filter 包含 D:删除一个翻译文件同样要触发检查。
# 检查对象是 Git 索引里的暂存快照(git checkout-index 直接读索引,不碰工作区/索引本身),
# 而不是工作区当前文件——避免"暂存坏版本、工作区又改回好版本"绕过检查。
i18n_files=$(mktemp)
git diff --cached --name-only -z --diff-filter=ACMRD -- \
"src/locales/*.json" "src/assets/_locales/*.json" > "$i18n_files"

if [ -s "$i18n_files" ]; then
rm -f "$i18n_files"
snapshot_dir=$(mktemp -d)
node scripts/git-staged-snapshot.mjs "$(pwd)" "$snapshot_dir" && pnpm run check:i18n -- "--root=$snapshot_dir"
status=$?
rm -rf "$snapshot_dir"
[ $status -eq 0 ] || exit 1
else
rm -f "$i18n_files"
fi

# 在 main 或 release/* 分支上提交时额外跑测试
branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$branch" = "main" ] || echo "$branch" | grep -q "^release/"; then
Expand Down
18 changes: 17 additions & 1 deletion docs/translation.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,20 @@
2. 使用目标语言的自然表达;对同一 ScriptCat 概念使用规范中的固定术语,不要基于相近措辞合并不同的脚本类型。
3. 对需结合语境的术语,先核对实际功能、控件类型与上下文文案再决定用词。
4. 保留 i18next 插值、程序标识符、HTML/React 标记、URL 与元数据标识符(`@match`、`@require` 等)。
5. 完成后复查本次修改,确认符合对应术语规范,并检查命名一致性、名词/动词混用等问题。
5. 完成后复查本次修改,确认符合对应术语规范,并检查命名一致性、名词/动词混用等问题。
6. 运行 `pnpm run check:i18n`(或 `pnpm lint`,已内含此检查),确认没有遗漏或多余的翻译 key。

## 机械检查:遗漏翻译 / Mechanical check: missing translations

`scripts/check-i18n.mjs`(`pnpm run check:i18n`)在每次 `pnpm lint` / `pnpm lint:ci` 时自动运行,用于捕获人工审阅容易漏掉的问题:

- `src/locales/locales.ts`:`src/locales/` 下的每一个 locale 目录都必须被 `import * as X from "./<locale>"` 并在 `resources` 中以自己的 locale code 展开注册;一个目录只存在于磁盘、没有接入 `locales.ts` 会导致检查失败。顶层 `NS` 数组也必须与 `en-US/` 下的命名空间文件集合完全一致,多一个或少一个都会报错。
- `src/locales/<locale>/*.json` 中每个 key 是否与 `en-US`(模板 / fallback 语言)的 key 集合一一对应,缺失或多余的 key 都会报错。
- `src/locales/<locale>/index.ts` 是否(以真实的 `export ... from "./<ns>.json"` 语句,而非文本匹配)导出了 `en-US` 拥有的全部命名空间。
- `src/assets/_locales/<chrome-locale>/messages.json`(`chrome.i18n` 语言文件,见上文"翻译工作流"一节)是否与 `en/messages.json` 的 key 一致;**`src/locales/` 下的每一个 locale 都必须有对应的 `_locales` 目录**,缺失会导致检查失败。
- `docs/references/terminology-<locale>.md` 是否存在:**`src/locales/` 下的每一个 locale 都必须有对应的术语规范文件**,缺失会导致检查失败——不允许新增或修改某个 locale 却不提交其 `terminology-<locale>.md`。
- `src/pkg/utils/monaco-editor/langs/`(或历史上未拆分时的单文件 `langs.ts`)中 `editorLangs`(编辑器悬浮提示、脚本头字段提示等)的 key 是否与 `en-US` 一致;**`src/locales/` 下的每一个 locale 都必须有对应的 `editorLangs` 条目**,缺失或 key 集合不一致都会导致检查失败。

这个脚本无法判断翻译措辞是否准确、是否符合术语规范——那部分仍需人工审阅并遵循本文件与对应的 `terminology-<locale>.md`;它只保证不会有 key、注册项或术语规范文件被整段遗漏。检查本身是 fail-closed 的:任何它无法静态解析的结构(`spread`、计算属性、`satisfies`、语法错误、循环引用等)都会报错,而不会被静默放行。

提交时机上,`git commit` 触发的 `.husky/pre-commit` 校验的是 **Git 暂存区(`git add` 之后的内容)**,不是工作区当前文件——`pnpm lint` / `pnpm run check:i18n` 手动运行时校验的才是工作区文件。
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"build": "cross-env NODE_ENV=production rspack build",
"pack": "node ./scripts/pack.js",
"typecheck": "tsc --noEmit",
"lint": "concurrently -g \"prettier --check --cache \\\"**/*.{ts,tsx,js,jsx,mjs}\\\"\" \"tsc --noEmit\" && eslint . --cache --cache-location .eslintcache",
"lint:ci": "concurrently -g \"prettier --check --cache \\\"**/*.{ts,tsx,js,jsx,mjs}\\\"\" \"tsc --noEmit\" && eslint . --cache --cache-location .eslintcache",
"lint": "concurrently -g \"prettier --check --cache \\\"**/*.{ts,tsx,js,jsx,mjs}\\\"\" \"tsc --noEmit\" \"pnpm run check:i18n\" && eslint . --cache --cache-location .eslintcache",
"lint:ci": "concurrently -g \"prettier --check --cache \\\"**/*.{ts,tsx,js,jsx,mjs}\\\"\" \"tsc --noEmit\" \"pnpm run check:i18n\" && eslint . --cache --cache-location .eslintcache",
"lint-fix": "concurrently -g \"prettier --write --cache \\\"**/*.{ts,tsx,js,jsx,mjs}\\\"\" \"tsc --noEmit\" && eslint --fix . --cache --cache-location .eslintcache",
"test": "vitest --no-coverage --reporter=verbose",
"test:ci": "vitest run --no-coverage --reporter=default --reporter.summary=false",
Expand All @@ -25,7 +25,8 @@
"test:e2e": "pnpm exec playwright test",
"test:e2e:ui": "pnpm exec playwright test --ui",
"validate:yaml": "node ./scripts/validate-yaml.mjs",
"validate:yaml:all": "node ./scripts/validate-yaml.mjs --all"
"validate:yaml:all": "node ./scripts/validate-yaml.mjs --all",
"check:i18n": "node ./scripts/check-i18n.mjs"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
Expand Down
Loading
Loading