refactor: move runtime implementation out of spec protocol package#856
refactor: move runtime implementation out of spec protocol package#856
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…tor) - Add StrictObjectTranslation type utility for compile-time field/option enforcement - Add generateTranslationSkeleton() for AI-friendly translation templates - Add validateTranslationCompleteness() for runtime completeness validation - Make ObjectSchema.create() generic to preserve field key types - Update zh-CN/en/ja-JP translations with missing category and recurrence_type options - Add satisfies pattern to zh-CN.ts as reference example - Add comprehensive unit tests (27 new tests in spec, 52 in example) Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Simplify ExtractOptionValues to use only ReadonlyArray (supertype of Array) - Rename ambiguous 'obj' parameter to 'value' in checkPlaceholderResidue - Use Omit<ServiceObject, 'fields'> & Pick<T, 'fields'> to avoid type conflicts Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Move generateTranslationSkeleton() and validateTranslationCompleteness() out of packages/spec per spec's "no business logic" principle. Spec retains only protocol definitions: - StrictObjectTranslation type utility (translation-typegen.ts) - TRANSLATE_PLACEHOLDER constant (translation-skeleton.ts) - All existing Zod schemas (translation.zod.ts) Runtime implementations should be re-implemented in appropriate packages (CLI, service-i18n, metadata) in future work. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Refactors the spec protocol package to keep it definition-only by removing i18n runtime implementations, while retaining protocol-level constants and type utilities for compile-time translation completeness checks. Updates the Todo example translations accordingly and adds a completeness test.
Changes:
- Adds
StrictObjectTranslation<T>type utility (and tests) for compile-time enforcement of field/option translation completeness. - Strips translation skeleton functionality down to the protocol constant
TRANSLATE_PLACEHOLDERand exports new system modules. - Updates Todo example translations to include missing select option maps and adds a translation completeness test.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/spec/src/system/translation-typegen.ts | Introduces strict translation types (type-level only). |
| packages/spec/src/system/translation-typegen.test.ts | Adds tests for the new type utility (but currently relies on typechecking). |
| packages/spec/src/system/translation-skeleton.ts | Keeps only the protocol placeholder constant. |
| packages/spec/src/system/index.ts | Exports the new typegen and skeleton constant modules. |
| packages/spec/src/data/object.zod.ts | Adjusts ObjectSchema.create typing to preserve literal field keys for type-level tooling. |
| examples/app-todo/src/translations/zh-CN.ts | Applies satisfies StrictObjectTranslation<typeof Task> and fills missing select options. |
| examples/app-todo/src/translations/en.ts | Adds missing select option translations (category, recurrence_type). |
| examples/app-todo/src/translations/ja-JP.ts | Adds missing select option translations (category, recurrence_type). |
| examples/app-todo/src/translations/translation-completeness.test.ts | Adds a locale translation completeness test (currently Vitest-based). |
Comments suppressed due to low confidence (1)
packages/spec/src/system/translation-typegen.test.ts:110
- This test file uses
expectTypeOfand@ts-expect-errorto validate type-level behavior, butpackages/specrunsvitest runwithout any typechecking step (Vitest transpiles via esbuild and won’t enforce these assertions). Add a typecheck phase (e.g.,vitest --typecheckortsc --noEmitin CI/scripts) so these type-safety tests actually fail when the types regress.
it('should report TS error when a field is missing', () => {
type T = StrictObjectTranslation<typeof SimpleObject>;
// @ts-expect-error — missing 'email' field
const _invalid: T = {
label: 'Test',
fields: {
name: { label: 'Name' },
},
};
expect(_invalid).toBeDefined();
});
| import { describe, it, expect } from 'vitest'; | ||
| import { Task } from '../objects/task.object'; | ||
| import { en } from './en'; | ||
| import { zhCN } from './zh-CN'; | ||
| import type { TranslationData } from '@objectstack/spec/system'; | ||
|
|
There was a problem hiding this comment.
examples/app-todo imports vitest here, but the app package does not declare vitest in devDependencies and its test script runs objectstack test (QA runner), not Vitest. Also tsconfig.json includes src/**/*, so pnpm typecheck will fail with “Cannot find module 'vitest'”. Either add Vitest (and a Vitest-based test script/config) to this package, or move this check into the existing QA/Node test harness so it actually runs.
packages/specis the protocol package — it should contain only definitions (Zod schemas, TypeScript types, constants). ThegenerateTranslationSkeleton()andvalidateTranslationCompleteness()runtime functions added in the previous PR violate the "no business logic" principle.Removed from spec
translation-skeleton.ts— strippedgenerateTranslationSkeleton()function; retainedTRANSLATE_PLACEHOLDERconstant (protocol convention)translation-validator.ts— deleted entirely (validateTranslationCompleteness(),ValidationResult, and all helpers)index.tsexport oftranslation-validatorRetained in spec (definitions only)
translation-typegen.ts— pure types (StrictObjectTranslation<T>) ✅TRANSLATE_PLACEHOLDERconstant — protocol-level skeleton marker ✅translation.zod.ts— all Zod schemas unchanged ✅zh-CN.tssatisfiespattern + completeness test — depend only on types ✅Runtime implementations should be re-added in CLI (
objectstack i18n extract),service-i18n, ormetadatapackages.What stays in spec after this change
Original prompt
This section details on the original issue you should resolve
<issue_title>为翻译准确性开发“对象字段/选项完整性约束”与AI友好翻译骨架工具</issue_title>
<issue_description>## 背景
当前翻译文件(如
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