Skip to content

feat(i18n): assign the locale paths outside UiCatalog<T> before ko integration #5075

Open

Description

@heeoneie

Problem

Per the updated maintainer decision on #3975, Korean will roll out progressively. Until each translation slice lands, an untranslated Korean surface may intentionally resolve to English. This audit checks that each locale path reaches the correct surface or the shared English fallback, and that it does not crash or silently render another language such as Simplified Chinese. It is a runtime-path and coverage audit, not a requirement that every string be translated before core enablement.

Those paths are not covered by the 126 UiCatalog<T> sites counted on #3975, and they split into two classes with different failure modes.

Class A — compiles clean, renders English

These pass typecheck with ko enabled and silently serve English, so nothing reports them.

Path Shape
apps/desktop/src/main/e2e-fixture.ts (parseLocaleFlag) no ko branch — returns null
apps/desktop/src/main/browser-message-box.ts locale ternary
apps/desktop/src/renderer/workhub-surface.ts locale ternary / if-chain
apps/desktop/src/renderer/settings/daily-review-settings-page.ts hardcoded union instead of UiLocale
apps/desktop/e2e/fixtures.ts hardcoded union instead of UiLocale
apps/desktop/.storybook/preview.ts and stories no ko in the locale global

I catalogued these on #3975 against c0229b0 and re-verified the paths today. The line numbers in that comment have drifted, so they are omitted here deliberately — search for the shape, not the line.

One entry did not survive re-verification: a locale if-chain in renderer/features/connection-settings/subscription-result-message.ts. No such file exists under apps/desktop/src/renderer/features/connection-settings/ at current main, so it was renamed or moved rather than fixed. It should be re-located, not assumed gone.

parseLocaleFlag is the same class @Astro-Han caught for zh-TW in #3853, which is the argument for tracking this once rather than rediscovering it per locale.

Class B — hard failures, but uncounted

Plain as const locale maps indexed with a UiLocale. They are not declared as UiCatalog<T>, so they are absent from the census, and they only fail once ko is in the union. From run 34312890700 on 36a4377 (#5011) — all TS7053, all in maka-agent (packages/cli):

  • src/pi-tui-todo.ts:131,188,217
  • src/pi-tui-transcript-viewer.ts:208
  • src/pi-tui-runner.ts:4005
  • src/__tests__/tui-copy-catalog.test.ts:59

Six is a floor, not a total. That run ends with tsc -p tsconfig.json failing in packages/cli with exit code 2, so any workspace built after it was never reached — whatever equivalents exist in @maka/ui and @maka/desktop are still unmeasured.

Desired outcome

Every path above has an owner and an explicit outcome before Korean enablement: translated Korean or a verified shared English fallback. No path may crash, mislabel another locale, or bypass the locale authority.

Proposed homes, for @me2seeks to confirm or overrule:

Acceptance criteria:

  • Every Class A path renders Korean or is verified to use the shared English fallback; paths that are intentionally English-only are recorded.
  • Every Class B map has a typed Korean value or resolves through an explicit, tested English fallback; no unsafe undefined index or implicit any remains.
  • English remains complete, and completeness/placeholder checks for shipped zh-CN and zh-TW remain enforced.
  • A full repo typecheck with ko enabled reports zero locale-related errors.
  • The Korean coverage inventory distinguishes translated copy from English fallback so the remaining work is visible.
  • The as const maps in @maka/ui and @maka/desktop are enumerated once the CLI gap no longer blocks the build.
  • The relocated subscription-result-message if-chain is found and assigned, or recorded as no longer existing.

Refs #3975, #5011. Part of #3974.

Alternatives or workarounds

Leave them where they are without validating the fallback. ko could then crash, show the wrong locale, or leave untracked English paths in desktop main, WorkHub, daily-review settings, fixtures, and Storybook.

Assigning fixes opportunistically without one audit still risks missing non-catalog locale branches. Track the path and its outcome once; Korean fallback is acceptable during this rollout only when the route is explicit, tested, and included in the coverage inventory.

Activity

  1. me2seeks commented on Sep 9, 2026

    @me2seeks
    Contributor

    Thanks for turning this into a concrete audit issue. The proposed split is confirmed, with these explicit owners:

    #5075 remains the cross-slice audit and release gate, not a catch-all implementation PR. Before the integration branch is cut, update this issue with the complete scan result and confirm that every Class A path renders Korean or is explicitly recorded as English-only, and every Class B map is typed or has a ko entry.

    @heeoneie, thank you for locating and separating these paths. The assigned slice owners, not this tracking issue, now carry the implementation work.

  2. heeoneie commented on Sep 16, 2026

    @heeoneie
    ContributorAuthor

    Correction: the central claim below is wrong — SHELL_COPY_BY_LOCALE is
    type-constrained after all. See this comment.

    While scoping slice 1 (#3977) I ran into a locale map that doesn't fit either class cleanly, and I think it points at a gap in how the audit plans to find them.

    apps/desktop/src/renderer/locales/shell-copy.ts declares its catalog with no type at all:

    const SHELL_COPY_BY_LOCALE = {

    Every other catalog in slice 1 is constrained — satisfies UiCatalog<SettingsPreferencesCopy>, : UiCatalog<OnboardingCatalog> =, satisfies UiCatalog<ShellRemainingCopy>, satisfies UiCatalog<PermissionCenterCopy>. This one is not.

    That gives it a split character:

    • It behaves like Class A. A ko entry can be added while any key left out of it silently serves English, because getShellCopy() returns SHELL_COPY_BY_LOCALE[locale] and nothing narrows that to UiCatalog<ShellCopy>.
    • Its cause is Class B — an untyped locale map. But it will not fail typecheck when ko joins the union, because there is no type to violate.

    That combination is the problem for the acceptance criteria as written. The plan is to surface Class B by typechecking each workspace once ko is in. This file passes that sweep and ships English anyway.

    Concretely, two module-level constants live outside the en block and are referenced from it: EN_STATIC_COMMANDS (70 strings) and EN_SETTINGS_SECTIONS (16 strings). ko needs its own of both and nothing forces it — 86 strings that a typecheck-driven audit would not ask for.

    Suggested addition to the audit: alongside the typecheck sweep, scan for locale-keyed catalog objects that carry no UiCatalog<T> annotation and no satisfies clause. Those are invisible to typecheck by construction, so the sweep cannot find them no matter how thorough it is.

    I'm covering shell-copy.ts as part of slice 1. Flagging it here so the other slice owners can check their own files for the same shape before the integration branch is cut.

    Measured on stack/i18n-ko-core at a193ad8d1.

  3. heeoneie commented on Sep 16, 2026

    @heeoneie
    ContributorAuthor

    Correction to my comment above — I got the central claim wrong.

    SHELL_COPY_BY_LOCALE is constrained. The opening line at L757 carries no
    annotation, but the object closes with } satisfies UiCatalog<ShellCopy>; at
    L2313 — 1,556 lines later — and ShellCopy declares both commands (L414)
    and settingsSections (L415). I read the opening line and not the closing
    one.

    Checked by running it this time rather than by reading: with ko in
    UI_LOCALES and the catalog untouched, tsc reports
    shell-copy.ts(2280,3): error TS2741: Property 'ko' is missing. All five
    slice-1 catalogs are constrained the same way. So the typecheck sweep in the
    acceptance criteria does find this file, and the hole I described is not
    there. Sorry for the noise.

    What survives is narrower, and I think still worth having. The type
    enforces that the keys exist, not that the values were translated. This
    compiles clean:

    ko: { …, commands: EN_STATIC_COMMANDS, settingsSections: EN_SETTINGS_SECTIONS }

    — 86 English strings shipped to Korean users with a green typecheck.

    That is not hypothetical. zh-CN and zh-TW currently share one ZH_* pair
    for exactly these two constants, which is why Traditional users see
    Simplified text in the command palette and the settings section names
    (#5377). The same shape would let a ko entry point at EN_* and pass.

    So the gap isn't "untyped catalogs the sweep misses" — it's "locale-keyed
    constants whose values the type system cannot check." A cheap mechanical
    check covers it: for each catalog, flag any two locales that reference the
    same module-level constant. That catches the zh case that exists today and
    a ko-pointing-at-EN_* case tomorrow. It's a much smaller addition than
    what I proposed above, and it's the only part of that comment I'd still
    stand behind.

  4. Astro-Han commented on Sep 17, 2026

    @Astro-Han
    Contributor

    Closing this as an issue for now: the direction has not been confirmed by a maintainer, and the tracker only keeps proposals that already have an agreed scope. Please continue it in GitHub Discussions (https://github.com/apache/maka/discussions); once the shape and acceptance criteria are settled there, open an issue with that scope and it will be picked up.

  5. heeoneie commented on Sep 18, 2026

    @heeoneie
    ContributorAuthor

    Two more entries for this issue, found while trying to take the ko
    screenshots #3977 asks for. Both are on stack/i18n-ko-core at a193ad8d1,
    and the line numbers are from there.

    Class A — the Storybook locale global (apps/desktop/.storybook/preview.tsx)

    This is the entry the issue already lists as "no ko in the locale global",
    now with the exact shape. Two places, and only the second one is visible:

    • preview.tsx:41 — the decorator maps the global to a UiLocale with a
      ternary chain that ends in a default:

      const locale =
        context.globals.locale === 'en'
          ? 'en'
          : context.globals.locale === 'zh-TW'
            ? 'zh-TW'
            : 'zh-CN';

      ko is not a branch, so it falls through to 'zh-CN'. Nothing fails; the
      story renders Simplified Chinese while the toolbar claims something else.

    • preview.tsx:103 — the toolbar items array lists three locales, so ko
      cannot be picked from the UI at all.

    Whoever takes #3980 needs both. Adding the branch without the toolbar entry
    leaves ko unreachable, and adding the toolbar entry without the branch
    silently shows zh-CN under a Korean label — the second failure is the one
    this issue is about.

    The crash behind it (packages/ui/src/astryx-i18n.tsx:68)

    Adding both locally does not give a Korean story. The global decorator mounts
    AstryxLocaleProvider, which throws for ko:

    TypeError: Cannot read properties of undefined (reading 'ko')
      at packages/ui/src/astryx-i18n.tsx:59
      at AstryxLocaleProvider (packages/ui/src/astryx-i18n.tsx:58)
    

    The line is:

    const base = astryxMessageOverrides(locale)[locale];

    astryxMessageOverrides (line 104) already takes the locale and returns
    OVERRIDES_BY_LOCALE[locale], and the call site indexes that result by the
    locale a second time. The double indexing is why the failure reads as
    undefined[...] rather than as a missing catalog entry: the outer lookup
    returns undefined for ko, and the inner one then throws.

    It is worth listing here because it handles a locale outside the
    UiCatalog<T> contract that the 126 sites on #3975 were counted against. It
    typechecks today only because OVERRIDES_BY_LOCALE is declared with
    satisfies UiCatalog<Overrides> and the index happens on the function's
    return type, so the census does not see it.

    Why this blocks the desktop app too, not just Storybook

    Same line, same cause. On a machine whose system locale is ko_KR the app
    resolves ko without anyone touching the language setting, and the renderer
    crashes in AstryxLocaleProvider before the first surface paints. Guarding
    that one call locally moves the crash to the next catalog the tree reaches
    (session-collaboration-copy.ts, then a main-process catalog), because
    catalog accessors index without a fallback: return CATALOG[locale];.

    That is the right default — it fails loudly instead of shipping the silent
    English this issue is about — but it does mean a partially translated stack
    cannot be run at all, and therefore that no ko screenshot can be taken
    until the sibling slices land. I recorded the same chain on #5451.

  6. me2seeks commented on Sep 29, 2026

    @me2seeks
    Contributor

    Maintainer decision update: progressive Korean rollout is now accepted; see the updated policy on #3975 and the implementation direction on #5100. I have reopened this audit because its direction is now confirmed.

    During rollout, an untranslated path may use the shared English fallback. The audit should verify that every path either renders Korean or reaches that fallback safely, and that no path crashes or silently renders Simplified Chinese under a Korean locale. Keep the coverage inventory explicit so users and maintainers can see which surfaces are still English. Preserve complete English and the existing Simplified/Traditional Chinese coverage checks. This issue does not require full Korean coverage before the core locale can land.

    The per-slice owners remain responsible for their assigned paths; maintainers will coordinate the full-workspace check, integration branch, screenshots, and E2E. The final Korean coverage state remains part of the #3974 completion record.

    简体中文

    维护者更新决定:现已接受韩语渐进式上线,详见 #3975 的交付政策和 #5100 的实施方向。方向已经确认,因此重新开启本审计。

    渐进上线期间,未翻译路径可以使用共享的英文回退。本审计要确认每条路径都能显示韩语或安全回退英文,不能崩溃,也不能在韩语界面错误显示简体中文。覆盖清单需要明确列出仍显示英文的界面。英文保持完整,简体和繁体中文的既有完整性检查继续保留。本 issue 不要求核心合入前先完成所有韩语翻译。

    各分片负责人仍处理各自路径;完整工作区检查、集成分支、截图和 E2E 由维护者协调。最终韩语覆盖情况记录在 #3974。

  7. github-actions commented on Sep 29, 2026

    @github-actions
    No description provided.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions