Measured on @objectstack/spec17.2.0.
Note: placeholders below are written ACTION / PARAM rather than in angle brackets — GitHub's body sanitizer strips <a>-shaped fragments, which silently ate them on the first revision of this issue.
TranslationItemSchema declares, per action:
_actions.ACTION.label
_actions.ACTION.description
_actions.ACTION.confirmText
_actions.ACTION.successMessage
_actions.ACTION.params.PARAM.{ label, helpText, placeholder, options }
_actions.ACTION.resultDialog.{ title, description, acknowledge, fields }
and @objectstack/rest's validateTranslationReferencesvalidates the keys of both description and params — checkActionParams reports a param key the action does not declare, with a hint naming the declared params.
But translateAction overlays only label, confirmText, successMessage and resultDialog. Reproduced directly against the shipped resolver:
constbundle={'zh-CN': {objects: {duly_task: {_actions: {duly_task_skip: {label: '跳过',description: '动作描述',confirmText: '确认',successMessage: '已跳过',params: {skip_reason: {label: '原因',helpText: '帮助',placeholder: '占位'}},}}}}}};translateAction(action,bundle,{locale: 'zh-CN'});label: '跳过' ← translated
confirmText: '确认' ← translated
successMessage: '已跳过' ← translated
description: 'D' ← NOT translated
params[0]: { label: 'Why skipped', helpText: 'H', placeholder: 'P' } ← NOT translated
The asymmetry is what makes this expensive: the keys are declared, a linter checks them and suggests corrections for them, so an author has every reason to believe they work. An action's description is its tooltip/subtitle, and params[].label / .placeholder / .helpText are the entire parameter dialog — for duly_task_skip that dialog is the only place the reason can be entered, so a Chinese deployment gets a Chinese button opening an English form.
Same asymmetry for the globalActions branch.
Expected:translateAction overlays description, and each params[i] matched by name (falling back to field, the way checkActionParams collects them) for label / helpText / placeholder / options.
The application (objectstack-ai/duly) authors these keys today rather than leaving them empty — they are the spec's own keys, they are served to REST/OpenAPI/MCP callers reading the action, and they start rendering the moment this lands. Recorded there in src/translations/authored-text.ts with the measurement above.
Filed from objectstack-ai/duly#18.
Generated by Claude Code
Measured on
@objectstack/spec17.2.0.TranslationItemSchemadeclares, per action:and
@objectstack/rest'svalidateTranslationReferencesvalidates the keys of bothdescriptionandparams—checkActionParamsreports a param key the action does not declare, with a hint naming the declared params.But
translateActionoverlays onlylabel,confirmText,successMessageandresultDialog. Reproduced directly against the shipped resolver:The asymmetry is what makes this expensive: the keys are declared, a linter checks them and suggests corrections for them, so an author has every reason to believe they work. An action's
descriptionis its tooltip/subtitle, andparams[].label/.placeholder/.helpTextare the entire parameter dialog — forduly_task_skipthat dialog is the only place the reason can be entered, so a Chinese deployment gets a Chinese button opening an English form.Same asymmetry for the
globalActionsbranch.Expected:
translateActionoverlaysdescription, and eachparams[i]matched byname(falling back tofield, the waycheckActionParamscollects them) forlabel/helpText/placeholder/options.The application (
objectstack-ai/duly) authors these keys today rather than leaving them empty — they are the spec's own keys, they are served to REST/OpenAPI/MCP callers reading the action, and they start rendering the moment this lands. Recorded there insrc/translations/authored-text.tswith the measurement above.Filed from
objectstack-ai/duly#18.Generated by Claude Code