Skip to content

fix(service-settings): the Default timezone help says what the setting does, in every locale - #22174

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-22136-timezone-help-plain
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-22136-timezone-help-plain

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22136

Clause-②: no

The Default timezone help that an administrator reads in Settings → Localization and in the first-run timezone prompt (objectstack-ai/objectui#11758) named formula functions and an analytics term. It now says what the setting does, in plain words, in the manifest and in all four built-in packs. This is the wording half of the maintainer's ask; the browser-zone pre-selection half already shipped in objectui. The maintainer's words:

首次进入应用时,时区设置弹窗写的是 "IANA zone for today()/daysFromNow…"。应改成白话,并自动预选浏览器所在时区。

What changed, per served string

Where Before After
manifest description (localization.manifest.ts) IANA zone used to resolve today()/daysFromNow, analytics date buckets, and rendered datetimes. Decides what "today" means in formulas, how reports group dates, and how dates and times are shown. Accepts any IANA time zone name (e.g. Europe/Paris).
en help IANA zone for today()/daysFromNow, analytics date buckets, and rendered datetimes. Decides what "today" means in formulas, how reports group dates, and how dates and times are shown. Accepts any IANA time zone name (e.g. Europe/Paris).
es-ES help Zona IANA usada para resolver today()/daysFromNow, los buckets de fecha de analítica y las fechas/horas renderizadas. Determina qué significa «hoy» en las fórmulas, cómo agrupan las fechas los informes y cómo se muestran las fechas y horas. Acepta cualquier nombre de zona horaria IANA (p. ej. Europe/Madrid).
ja-JP help today()/daysFromNow の解決、分析の日付バケット、日時レンダリングに使う IANA タイムゾーン。 数式での「今日」がどの日か、レポートでの日付のまとめ方、日付と時刻の表示方法を決めます。任意の IANA タイムゾーン名(例:Asia/Tokyo)を指定できます。
zh-CN help 用于 today()/daysFromNow、分析日期分桶和 datetime 渲染的 IANA 时区。 决定公式中的"今天"是哪一天、报表如何按日期分组,以及日期和时间如何显示。可填写任意 IANA 时区名称(如 Asia/Shanghai)。

Why "IANA" stays, as a second sentence: the field is a free-text combobox (objectui SettingsField's DomainCombobox, because the key declares valueDomain: 'iana_time_zone'), and the server accepts any IANA zone, not only the 17 listed. An administrator who types a zone needs to know the format. The first sentence carries no standard name.

No setting key, default (UTC), accepted value or behaviour changes. No packages/spec, no settings-service*.ts.

Which layer is served (and so where the pin reads)

The console's useSettingsLabel().fieldHelp resolves settings.localization.keys.timezone.help from the bundles the settings plugin loads into the i18n service at kernel:ready, and falls back to the manifest description that GET /api/settings/localization serves. Both layers carried the developer wording, so both are rewritten and both are pinned. The four packs are hand-written: no generator feeds service-settings/src/translations/*.ts (no *.generated.ts there; check:i18n and check:i18n-stale-fill scan only the generated bundle sets).

The pin

packages/services/service-settings/src/localization-timezone-help.pin.test.ts boots the real SettingsServicePlugin (its default manifests and translations) on a LiteKernel beside a stub i18n service that records every loadTranslations(locale, data) call. It then reads:

  1. the help in every bundle the plugin loaded (the locale set is whatever the plugin loads, with a floor of en / zh-CN / ja-JP / es-ES so the loop cannot pass over nothing); the help must be present in each locale;
  2. the timezone specifier's description from GET /api/settings/localization, mounted on the service that boot registered.

It asserts each names no function identifier: no name directly followed by an opening parenthesis, and no lowerCamelCase name. A detector control asserts the old wording is flagged (today( and daysFromNow), so the detector cannot pass vacuously. The new wording itself is not pinned.

Reverse verification, at commit 40fd214174, both legs through scripts/ablation-replace.mjs (anchor hit 1 → 0, blob changed, restore proven blob == HEAD and git diff HEAD empty):

  • ja-JP help put back to the old string: × the translated help, in every locale the plugin loads with ja-JP: today()/daysFromNow の解決… expected [ 'today(', 'daysFromNow' ] to deeply equal []; Tests 1 failed | 3 passed (4).
  • manifest description put back: × the manifest description served by GET /api/settings/localization with expected [ 'today(', 'daysFromNow' ] to deeply equal []; Tests 1 failed | 3 passed (4).

Both went red in the expected direction. The tests import the subject by relative src/ paths, so no dist/ rebuild was involved.

Verification (head 40fd214174)

  • pnpm --filter @objectstack/service-settings exec vitest run --maxWorkers=2: Test Files 37 passed (37), Tests 632 passed (632).
  • pnpm --filter @objectstack/service-settings typecheck: exit 0; tsc --noEmit --listFiles includes the new pin and all five edited files (37 test files in the program).
  • pnpm --filter @objectstack/service-settings build: check-dts-emitted: @objectstack/service-settings - 2/2 declared declaration file(s) present.
  • Dependency closure built first: turbo run build --filter='@objectstack/service-settings^...', 15/15 tasks.
  • Gates: node scripts/pm/dispatch-gates.mjs --commands (no paths) derived 63 families; all 63 run. 62 exit 0. One is NOT MEASURED: check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (it reads every package's dist/, and only the closure was built here). --ran reconciliation: 63 derived famil(ies) accounted for — 62 run, 1 NOT-MEASURED. Also run, all exit 0: check:i18n (OK (9 package(s) — all bundles in sync…)), check:i18n-stale-fill (OK (10 bundle set(s) — no new stale fills, 0 baselined)), check:i18n-coverage (OK (13 config(s), 621 baselined untranslated string(s), none new)), check:i18n-walk-parity, check:settings-bind-window, check:nul-bytes.
  • Lint, narrowed and proven: eslint --no-inline-config --format json on the six touched .ts files gives 6 files, 0 errors, 0 warnings (no "file ignored" warning, so all six are in the linted set). This repo's eslint.config.mjs enables no type-aware linting (no parserOptions.project), so this diff cannot change the verdict on any untouched file. The repo-wide pnpm lint is left to CI.

Acceptance notes

  • Other Localization keys still use standard names and implementation terms in their help: locale ("BCP-47 locale for message catalogs and number/date formatting"), currency ("…render code-less amounts as plain numbers"), default_country ("ISO 3166-1 alpha-2 code"). They are not changed here. The bounded in-place rule needs all four conditions, and its second one (a mechanical fix whose shape a ruling already fixed) does not hold: each needs new wording in four locales, and for locale the new wording would first need its "message catalogs" claim checked. The new pin covers only the timezone key. Rewording the others is a ruling for the seat.
  • The packs disagree with each other on two keys this PR leaves alone. currency has no en help (en shows the two-sentence manifest literal), and zh-CN / ja-JP / es-ES carry only its first sentence. default_country carries "Used for address and phone defaults." in es-ES and ja-JP, but not in en or zh-CN.
  • The console's missing-translation warnings for these keys are tracked in i18n(zh-CN): "pillar" translated as 支柱 with pillar names left in English; timezone settings keys missing objectui#11801.

Generated by Claude Code

…locale

The timezone help named formula functions (today(), daysFromNow) and an
analytics term to administrators. The manifest description and the en,
es-ES, ja-JP and zh-CN help now say what the setting does, keeping the
IANA hint as a short second sentence because the field accepts any zone.

A pin reads the served layers (the bundles the plugin loads into i18n at
kernel:ready, and GET /api/settings/localization) and holds the help to
naming no function identifier in every loaded locale.

Claude-Session: https://claude.ai/code/session_01WkL6Eijt432S1Y7ekb6ovQ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 8 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 7ef50a4fbbf9c819a550b2943d2ac74a7bd39770 → packageMentionDocs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

1 participant