Skip to content

View labels never resolve a translation — resolveViewLabel reads two fields the served view document does not have #4854

Description

@yinlianghui

resolveViewLabel (packages/spec/src/system/i18n-resolver.ts:149) cannot resolve any view authored through the normal path. Every view label in a running console falls back to its English literal, no matter what the translation bundle contains. Found while translating a customer-facing app; the app's twelve _views translation blocks turned out to have never been read.

Filed unassigned — recording the finding, not claiming it.

Two independent mismatches, either one sufficient

function viewObjectName(view: ViewLike): string | undefined {
  return view.objectName ?? view.data?.object;      // :141
}

const objectName = viewObjectName(view);
if (!bundle || !objectName) return fallback;        // :156
const candidate = data?.objects?.[objectName]?._views?.[view.name]?.label;  // :159

This is what GET /api/v1/meta/view?object=crm_account actually serves:

{
  "name": "crm_account.account_gallery",
  "object": "crm_account",
  "viewKind": "list",
  "label": "Account Cards",
  "config": {
    "name": "account_gallery",
    "label": "Account Cards",
    "data": { "provider": "object", "object": "crm_account" }
  }
}
  1. The object never resolves. objectName is absent — the field is object. data is absent — it is nested at config.data. So viewObjectName returns undefined and line 156 returns the fallback before any lookup happens.

  2. The key would be wrong even if it did. The lookup uses view.name, which the served document namespaces to crm_account.account_gallery. Translations are keyed on the bare name — objects.crm_account._views.account_gallery.label — which is also what config.name carries.

Fixing only the first leaves every lookup missing; fixing only the second never runs.

Measured, not inferred

In an app with objects.crm_account._views.all_accounts.label = '全部客户' present in the served zh-CN bundle, the console renders All Accounts. The unit tests at i18n-resolver.test.ts:127 pass because they construct ViewLike objects by hand with objectName and a bare name — a shape the runtime never produces.

Impact

Every list view's switcher tab, on every object, in every locale. On a Chinese-only deployment this is the most visible remaining English on the screen: it sits across the top of every list page, above data that is otherwise fully translated.

There is no workaround at the app layer other than authoring the labels in Chinese directly in src/views, which hard-codes one language into metadata and defeats the other three locales.

Worth deciding together with

report and dataset have no translator at all — no entry in METADATA_DOCUMENT_TRANSLATORS, no key in TranslationDataSchema, and defineStack strips a reports: block while tsc rejects it as an excess property (TS2353). Dataset dimension and measure labels are what a dataset-bound dashboard widget renders its table column headers from, so those are permanently English too.

One clue suggesting the dataset case may not need a new surface: a dimension whose name matches a real field name on the bound object already renders the translated field label. In the same dashboard, current_grade (dimension name = field name) shows Chinese while account_name (dimension name ≠ field name name) shows Customer. If the resolver fell back to the field label whenever a dimension names one, most of these would resolve with no new translation surface at all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    pm:queuepriority:p0Critical: blocker, must ship before MVP

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions