背景
当前翻译文件(如 examples/app-todo/src/translations/zh-CN.ts)使用 TranslationData 类型,底层是 z.record(z.string(), FieldTranslationSchema) —— 只验证值的形状,不验证 key 的完整性。
这意味着:
- AI 生成翻译时漏了 5 个字段,TypeScript 不报错,Zod 也不报错
- 多写了一个不存在的字段 key,也不报错
- select/multiselect 的 option 翻译少了几个,同样静默通过
参考现状:
目标
建立三重防线,保证 AI/人工翻译 字段全覆盖、option 不缺不漏、格式严格合规:
Object 定义 (task.object.ts)
│
├──→ ① TypeScript satisfies 约束 ← tsc 编译报错(少/多字段)
│
├──→ ② AI 翻译骨架 JSON(填空题) ← 结构已锁死,AI 不可能加减 key
│
└──→ ③ Vitest 完整性测试 ← CI 自动拦截
开发任务
Task 1: 类型工具 translation-typegen.ts
新增文件: packages/spec/src/system/translation-typegen.ts
从 Object 定义自动推导严格翻译类型:
// 核心泛型:把 fields Record 的 key 提取出来,要求翻译必须覆盖每一个
type StrictFieldTranslations<Fields> = {
[K in keyof Fields]-?: // -? = 去掉可选,变成必填
Fields[K] extends { options: ... }
? { label: string; options: Record<OptionValue, string> } // select 字段:options 也必填
: { label: string; help?: string; placeholder?: string } // 普通字段:label 必填
};
type StrictObjectTranslation<Obj> = {
label: string;
pluralLabel?: string;
fields: StrictFieldTranslations<Obj['fields']>;
};
验收标准:
Task 2: 翻译骨架生成器 translation-skeleton.ts
新增文件: packages/spec/src/system/translation-skeleton.ts
从 Object 定义自动生成 AI 友好的 JSON 填空模板:
function generateTranslationSkeleton(objectDef: ServiceObject): string
输出示例(task 对象):
{
"label": "__TRANSLATE__: \"Task\"",
"pluralLabel": "__TRANSLATE__: \"Tasks\"",
"fields": {
"subject": { "label": "__TRANSLATE__: \"Subject\"" },
"status": {
"label": "__TRANSLATE__: \"Status\"",
"options": {
"not_started": "__TRANSLATE__: \"Not Started\"",
"in_progress": "__TRANSLATE__: \"In Progress\"",
"waiting": "__TRANSLATE__: \"Waiting\"",
"completed": "__TRANSLATE__: \"Completed\"",
"deferred": "__TRANSLATE__: \"Deferred\""
}
}
}
}
验收标准:
Task 3: 完整性校验函数 validateTranslationCompleteness()
新增文件: packages/spec/src/system/translation-validator.ts
function validateTranslationCompleteness(
objectDef: ServiceObject,
translation: unknown,
): { valid: boolean; errors: string[] }
校验维度:
- Zod 结构验证 —
ObjectTranslationNodeSchema.safeParse()
- 字段完整性 — 源 fields 中有但翻译中没有 → 报
缺失字段
- 字段多余性 — 翻译中有但源 fields 中没有 → 报
多余字段
- Option 完整性 — select 字段的每个 option value 都必须有翻译
- 残留检查 — 不允许
__TRANSLATE__ 占位符残留
验收标准:
Task 4: 改造 Todo 示例作为范例
修改文件: examples/app-todo/src/translations/zh-CN.ts
// Before:
import type { TranslationData } from '@objectstack/spec/system';
export const zhCN: TranslationData = { ... };
// After:
import type { StrictObjectTranslation } from '@objectstack/spec/system';
import { Task } from '../objects/task.object';
type TaskTranslation = StrictObjectTranslation<typeof Task>;
export const zhCN = {
objects: {
task: { ... } satisfies TaskTranslation,
},
// ...
};
验收标准:
Task 5: Vitest 完整性测试
新增文件: examples/app-todo/src/translations/translation-completeness.test.ts
const fieldNames = Object.keys(Task.fields);
const selectFields = Object.entries(Task.fields)
.filter(([_, f]) => Array.isArray(f.options))
.map(([name, f]) => ({ name, values: f.options.map(o => o.value) }));
describe.each([['en', en], ['zh-CN', zhCN]])('%s', (locale, t) => {
it.each(fieldNames)('field: %s', (name) => {
expect(t.objects?.task?.fields?.[name]?.label).toBeTruthy();
});
it.each(selectFields)('options: $name', ({ name, values }) => {
for (const v of values) {
expect(t.objects?.task?.fields?.[name]?.options?.[v]).toBeTruthy();
}
});
});
验收标准:
不涉及(Not in scope)
- 不修改现有 Zod Schema(
z.record 保持不变)
- 不修改现有
TranslationData / AppTranslationBundle 类型(向后兼容)
- 不涉及 views/actions/workflows 的翻译完整性(后续迭代)
- 不涉及 CLI 包的实际
objectstack 命令注册(本期只提供函数,CLI 集成后续)
相关文件
| 文件 |
操作 |
说明 |
packages/spec/src/system/translation-typegen.ts |
新增 |
严格类型推导工具 |
packages/spec/src/system/translation-skeleton.ts |
新增 |
AI 骨架生成函数 |
packages/spec/src/system/translation-validator.ts |
新增 |
完整性校验函数 |
packages/spec/src/system/index.ts |
修改 |
新增 export |
examples/app-todo/src/translations/zh-CN.ts |
修改 |
satisfies 范例 |
examples/app-todo/src/translations/translation-completeness.test.ts |
新增 |
完整性测试 |
子任务拆分建议
背景
当前翻译文件(如
examples/app-todo/src/translations/zh-CN.ts)使用TranslationData类型,底层是z.record(z.string(), FieldTranslationSchema)—— 只验证值的形状,不验证 key 的完整性。这意味着:
参考现状:
task.object.ts有 18 个字段,3 个 select 共 14 个 optionzh-CN.ts只有TranslationData松散类型约束目标
建立三重防线,保证 AI/人工翻译 字段全覆盖、option 不缺不漏、格式严格合规:
开发任务
Task 1: 类型工具
translation-typegen.ts新增文件:
packages/spec/src/system/translation-typegen.ts从 Object 定义自动推导严格翻译类型:
验收标准:
StrictObjectTranslation<typeof Task>可以正确推导出 18 个字段 key 为必填options的必填 keypackages/spec/src/system/index.ts导出expectTypeOf或@ts-expect-error)Task 2: 翻译骨架生成器
translation-skeleton.ts新增文件:
packages/spec/src/system/translation-skeleton.ts从 Object 定义自动生成 AI 友好的 JSON 填空模板:
输出示例(task 对象):
{ "label": "__TRANSLATE__: \"Task\"", "pluralLabel": "__TRANSLATE__: \"Tasks\"", "fields": { "subject": { "label": "__TRANSLATE__: \"Subject\"" }, "status": { "label": "__TRANSLATE__: \"Status\"", "options": { "not_started": "__TRANSLATE__: \"Not Started\"", "in_progress": "__TRANSLATE__: \"In Progress\"", "waiting": "__TRANSLATE__: \"Waiting\"", "completed": "__TRANSLATE__: \"Completed\"", "deferred": "__TRANSLATE__: \"Deferred\"" } } } }验收标准:
task.object.ts的对象定义 → 输出包含全部 18 个字段的骨架options映射description的字段自动生成help占位符ObjectTranslationNodeSchema.parse()验证(替换占位符后)Task 3: 完整性校验函数
validateTranslationCompleteness()新增文件:
packages/spec/src/system/translation-validator.ts校验维度:
ObjectTranslationNodeSchema.safeParse()缺失字段多余字段__TRANSLATE__占位符残留验收标准:
fields.{name}.options.{value}__TRANSLATE__残留 → errors 报告Task 4: 改造 Todo 示例作为范例
修改文件:
examples/app-todo/src/translations/zh-CN.ts验收标准:
tsc --noEmit编译通过tsc报错(在测试中用@ts-expect-error验证)Task 5: Vitest 完整性测试
新增文件:
examples/app-todo/src/translations/translation-completeness.test.ts验收标准:
pnpm test -- translation-completeness全部通过不涉及(Not in scope)
z.record保持不变)TranslationData/AppTranslationBundle类型(向后兼容)objectstack命令注册(本期只提供函数,CLI 集成后续)相关文件
packages/spec/src/system/translation-typegen.tspackages/spec/src/system/translation-skeleton.tspackages/spec/src/system/translation-validator.tspackages/spec/src/system/index.tsexamples/app-todo/src/translations/zh-CN.tsexamples/app-todo/src/translations/translation-completeness.test.ts子任务拆分建议
translation-typegen.ts类型推导translation-skeleton.ts骨架生成translation-validator.ts校验函数