Skip to content

lint: validate-translation-references has no _tabs leg — an authored key naming a filter-preset tab that does not exist warns nobody #13835

Description

@huangyiirene

Filed out-of-scope by the flight-⑧ sweep of skills/objectstack-i18n/** (#13815 / #13658) while establishing what translation-target-unknown actually reports. Not fixed there — that PR (#13833) is a documentation-only diff.

What is missing

packages/lint/src/validate-translation-references.ts walks the object branch of every bundle and reports an orphan key for fields, fields.*.options, _views, _sections, and _actions (plus params). It does not walk _tabs.

Section headers in the rule, in order:

711:      // ── objects.<name>.…
830:      // ── globalActions.<name>
865:      // ── apps.<name>[.navigation.<id>]
895:      // ── dashboards.<name>[.widgets.<id> | .actions.<url>]
938:      // ── flows.<name>[.screens.<node_id>[.fields.<field_name>]]

and inside the object branch the sub-walks are fields / options / _views / _sections / _actions. _tabs appears nowhere in the file.

Why that is a hole and not a scoping choice

_tabs is a fully live authorable surface, not a planned one:

  • ObjectTranslationDataSchema declares it (packages/spec/src/system/translation.zod.ts), keyed by ViewTabSchema.name, resolved by resolveTabLabel;
  • collectExpectedEntries harvests it — measured on a small fixture, objects.task._tabs.urgent.label is emitted (with source: 'view');
  • so os i18n check already demands a translation for a tab key, while nothing tells an author that a _tabs key they wrote names a tab that no longer exists.

That is exactly the asymmetry #11608 identified for flows — "an authored key naming a flow, screen node or field that does not exist warns nobody" — which was accepted and closed by adding the flows leg. _tabs is the same shape, one group over, and was missed because it arrived after the object branch was written.

The failure is the quiet one this rule family exists to prevent: rename a filter preset, the bundle keeps its old _tabs key, the tab bar renders in the source locale above a fully localized grid, and every gate stays green.

Suggested fix

Add a _tabs sub-walk beside _sections in the object branch, reporting translation-target-unknown against the tab names the stack actually declares (page.interfaceConfig.userFilters.tabs[].name, de-duplicated per object the way walkObjectTabs in packages/cli/src/utils/i18n-extract.ts already does). The universe builder that the object branch consumes needs the tab names added alongside views and sections.

Also worth checking in the same pass: whether the rule's own coverage of the object branch should be pinned against ObjectTranslationDataSchema's key set, so the next group added to the shape cannot land without a leg here.

Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions