Skip to content

feat(i18n-ko): desktop slice 1 — settings-preferences, onboarding, shell, permissions - #5451

Draft
heeoneie wants to merge 6 commits into
apache:stack/i18n-ko-corefrom
heeoneie:feat/i18n-ko-desktop-slice1
Draft

heeoneie wants to merge 6 commits into
apache:stack/i18n-ko-corefrom
heeoneie:feat/i18n-ko-desktop-slice1

Conversation

@heeoneie

@heeoneie heeoneie commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Refs #3977

The Korean catalogs for the four desktop surfaces #3977 scopes, plus the
language selector rows that make them reachable.

This sits on the shared core base from #5100 and does not typecheck on
its own. The other slices' catalogs still have no ko entry, so the
workspace typecheck stays red until the stack is assembled. That is the
stack boundary, not a defect in this branch. Within this branch, each
translation commit removes exactly the missing-ko errors its file was
producing and adds none (see Verification).

Catalog Strings Translated
settings-preferences-copy.ts 325 321
onboarding-copy.ts 31 29
shell-copy.ts 524 499
shell-remaining-copy.ts 56 55
permission-center-copy.ts 114 102
Total 1,050 1,006

Counted on the en copy at the base. The remaining 44 strings are:

  • 16 enum keys
  • one empty string
  • 27 others: 14 shortcut glyphs, 10 search-keyword entries, one separator,
    and the second branch of two ternaries

shell-copy.ts also needed two new module-level constants,
KO_STATIC_COMMANDS (70) and KO_SETTINGS_SECTIONS (16). zh-CN and
zh-TW share one ZH_* pair; Korean has no variant to share with, so it
gets its own. Pointing ko at the EN_* constants would have
typechecked and shown 86 English strings in the Korean UI.

The language selector

#3977 requires Korean to be selectable. localeOptions is a plain array
that the UiCatalog<T> type does not constrain, so adding a ko block
alone leaves the selector without Korean and nothing fails. This PR
therefore also adds a ko row to the zh-CN, zh-TW and en catalogs,
following the existing convention: endonyms everywhere (한국어), and a
translated name only in en (Korean).

That is the only reason files outside the ko blocks are touched. The
en catalog grows by two strings, the 'ko' code and 'Korean'.

Three English-only branches removed

Korean has no case distinction and no plural agreement, so three
expressions in the templates do nothing once the locale is Korean:

  • permission-center-copy.ts: ${label.toLowerCase()}${label}
  • settings-preferences-copy.ts: a singular/plural ternary → one template
  • shell-copy.ts: a singular/plural ternary → one template

zh-CN and zh-TW already do the same in these places. Otherwise, every
placeholder keeps the name and count it has in the English. Because of
the last change, the Korean shell-copy.ts has 523 strings against 524.

One string needs a maintainer's call

app.useSkillPrompt (Use the ${skillName} skill:) is inserted into the
composer at app-shell.tsx:973 and reaches the model, so it is prompt
text as much as UI copy. I translated it like the rest, but I could not
verify how the model handles the Korean prefix. If you would rather keep
this one in English so skill invocation stays on the path it was tuned
for, say so and I will revert just this string.

How the copy was decided

  • Core anchors. The copy is anchored on the Korean strings already in
    the core layer (redaction.ts, relative-time.ts,
    tool-quiet-preview.ts): 14 fixed values this slice must not
    contradict. They apply to status labels, not to the same English word
    used as a verb, so could not be written is not forced into the
    written label.

  • Cross-domain terms first. Terms that cross domain boundaries were
    fixed before translating. Their frequencies were measured by a script
    over the base commit rather than estimated, so the table is
    reproducible rather than remembered. Where one English word covers two
    concepts, the Korean keeps them apart:

    • Remove (non-destructive) / Delete / Clear are three different
      words.
    • Diagnostics is 진단 정보 for the data and 진단 for a command
      group.
  • OS surface names. These follow what macOS actually shows in
    Korean, because the copy sends users to find them there:
    시스템 설정, 손쉬운 사용, 화면 및 시스템 오디오 녹음,
    개인정보 보호 및 보안. The names were read from the macOS 26.6
    system .loctable resources. Screen Recording in the English copy
    is the pre-macOS 15 name; see below.

  • Instruction endings. The Korean distinguishes guidance from a
    request.

    • A sentence that follows a product failure and asks the user to act
      again ends in …해 주세요.
    • Guidance, and requests to correct the user's own input, end in
      …하세요.
    • A message that covers both kinds of cause uses …해 주세요.
    • The rule was written down after the shell catalog was translated;
      all five catalogs were then re-checked against it. That is the last
      commit (one string in onboarding).
  • Consonant-dependent particles. Korean particles such as 을/를
    change form with the preceding word's final consonant, which a
    placeholder's value makes unknowable. Sentences are reordered so no
    such particle follows a placeholder, for example
    `“${name}” 삭제할까요?`.

  • Four mechanical checks ran over all five finished catalogs:

    1. Glossary terms appear in their fixed Korean form. Whole-string labels
      are compared exactly; inside sentences, the fixed stem must be present.
    2. Placeholder names and counts match the English.
    3. Sentence-final punctuation mirrors the English.
    4. No consonant-dependent particle sits directly after a placeholder.

    Results on the five catalogs as committed:

    • Checks 2–4: nothing flagged in any catalog.
    • Check 1: 12 hits.
      • 8 are documented exceptions recorded during review: 6 in
        shell-copy.ts and 2 in settings-preferences-copy.ts. Examples are
        key cap names left as printed on the keys, the adjective in
        clear sky, and the noun copy.
      • The other 4 are in the three smaller catalogs. 3 are false positives
        on inflected forms: 지원이 종료 for 지원 종료, for ,
        지울 for 지우. 1 is an approved wording, 생성할 수 없음 for
        Daily Review generation is unavailable.

    Check 1 has a known limitation. It matches the fixed Korean stem as a
    literal substring, so a correctly inflected form whose stem changes
    shape is reported as a mismatch and needs a human look. On
    shell-copy.ts, each checker was also verified by seeding a deliberate
    error and confirming that it was caught.

Verification

Renderer typecheck. tsc -p tsconfig.renderer.json --noEmit in
apps/desktop, run at the base and after each commit:

Commit Errors Removed Added
base a193ad8d1 611
onboarding 610 TS2741 (typed catalog, so no TS7053) none
permission center 608 TS2741, TS7053 none
shell remaining 606 TS2741, TS7053 none
settings preferences + selector 604 TS2741, TS7053 none
shell 602 TS2741, TS7053 none
onboarding request form 602 none none
  • The remaining 602 are not from this PR:
    • 98 Property 'ko' is missing errors in other slices' catalogs, plus
      their indexing errors.
    • 231 unresolved @maka/ui imports, because workspace packages were not
      built in my checkout.
  • Three of them sit in files this PR touches, and each is present
    identically at the base: one unresolved @maka/ui import in
    permission-center-copy.ts, and two missing exports from the built
    @maka/core in shell-copy.ts.

No English fallbacks. The Korean copy never points at the English
copy. Lines added on this branch contain no EN_* or enCopy reference.
In shell-copy.ts, the only identifiers in value position inside the
Korean copy are:

  • KO_STATIC_COMMANDS
  • KO_SETTINGS_SECTIONS
  • the shared STATIC_COMMAND_KEYWORDS

Structure. With string literals masked:

  • KO_STATIC_COMMANDS and KO_SETTINGS_SECTIONS match their EN_*
    counterparts token for token.
  • Each ko block matches its en block, except for the removed
    ternaries.

String counts. Every Korean catalog has the same keys and the same
number of strings as its English one, except shell-copy.ts (523 against
524, see above). The English strings that existed at the base are
unchanged.

Lint and format. biome check is clean on the five files.

Not run:

  • The test suites, because workspace packages were not built locally.
  • Repo-wide lint, format, build and typecheck. The typecheck is expected
    to stay red; see Summary.
  • Screenshots.

ci.yml, which provides the required test check, triggers only on pull
requests targeting main, so that check will not run here until the stack is
assembled. The Verification section above is the evidence until then.

Observations about the English copy

These are not change requests, and this PR does not touch them. They are
just things that surfaced while translating:

  • Screen Recording is the name macOS used before 15. The current pane is
    "Screen & System Audio Recording", and the Korean follows the current
    name so users can find it.
  • The en search-keyword arrays in shell-copy.ts carry a few Simplified
    Chinese entries, which look like a copy from the zh-CN row.
  • Plan mode and Plan Mode both appear. The Korean uses the proper-noun
    spelling consistently.

Remaining before this is ready

  • ko unit tests for the five catalogs
  • Screenshots at 1280 and 430
  • Repo-wide build and typecheck green — blocked on the rest of the
    stack, not on this branch

#3977's acceptance criteria include all three, which is why this is a draft
and why it says Refs rather than Fixes.

Not in scope

  • workHubHelp disagrees across en, zh-CN and zh-TW (workHubHelp copy disagrees across en, zh-CN, and zh-TW #5289).
    The Korean follows en pending that issue.
  • Shared Simplified constants. zh-CN and zh-TW share Simplified
    constants in the command palette and settings sections (zh-TW shows Simplified Chinese in the command palette and settings section names #5377).
  • Unread keys. Six keys in shell-remaining-copy.ts are read by no
    code. The type requires them, so they are translated.
  • Settings sidebar labels. They come from settings-navigation-copy.ts,
    which belongs to a later slice. The command palette's section names
    here use the same English. When that catalog is translated, its labels
    should use the same Korean as KO_SETTINGS_SECTIONS.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code — drafted the Korean copy for the five
catalogs, the verification scripts, and this description. The terminology
and every judgment call were reviewed and decided by me, and I verified
and submitted the result. Each of the six commits ends with a
Generated-by: Claude Code trailer. This repository merges by squash
only, and GitHub's default squash message lists the commit messages, so
the trailer is carried into the squash commit and remains its final
trailer. If the squash message is edited at merge time, please keep that
line as the last paragraph.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

No tests were added (ko unit tests are a remaining #3977 criterion).
biome passes on the changed files, and the per-file typecheck result is
under Verification. Repo-wide typecheck is expected to stay red on this
branch alone.

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

`ko` joined `UI_LOCALES` in the core layer, and
`ONBOARDING_COPY_BY_LOCALE` is annotated `UiCatalog<OnboardingCatalog>`,
so the renderer stopped typechecking at that catalog. Add the Korean
branch after `en`, the order the core catalogs already use.

The translation follows the English values rather than the key names.
`Enable` becomes `켜기`: the screen `needs_model` opens
(`settings/provider-connection-detail.tsx`) is a real switch, so no
separate wording for non-toggle actions is needed. `credentials` becomes
`인증 정보`, keeping the `인증` family of the core `인증 실패`.
`retired` becomes `지원 종료` so it stays distinct from `removed`
(`제거`) where `blocked:all_connections_retired` uses both in one body.
`Try again later.` is a request after a failure and takes the
`…해 주세요` form.

Verified with `tsc -p tsconfig.renderer.json --noEmit` in
`apps/desktop` before and after the change: the only difference is the
missing-`ko` TS2741 on this catalog going away, and no error is added.
The tree still reports unresolved `@maka/ui` imports and the other
catalogs without `ko`, which are left to their own commits. biome check
is clean on the file. Tests were not run.

Refs apache#3977

Generated-by: Claude Code
Add the Korean branch to `PERMISSION_CENTER_COPY` after `en`. The
catalog closes with `satisfies UiCatalog<PermissionCenterCopy>`, so the
missing `ko` surfaced twice: the TS2741 on the literal and a TS7053 in
`getPermissionCenterCopy`, whose `[locale]` index had no `ko` member.
Both go away with this branch.

macOS surface names use the Korean names System Settings shows, since
the user has to find them there: `시스템 설정`, `개인정보 보호 및 보안`,
`손쉬운 사용`, `자동화(Apple Events)`, and `화면 및 시스템 오디오 녹음`.
The last is what macOS 15 and later list for the pane
`Privacy_ScreenCapture` opens; the English `Screen Recording` is the
older name. The zh branches already localize these labels.

A few English words are split so they stay distinct on this page:
`configuration` is `구성` against `Settings` as `설정`, `action` is `동작`
against `task` as `작업`, and `revoke` is `철회` against `Cancel` as
`취소`. `capability` has no single rendering; the page title and
section read `권한과 기능 상태` / `기능 상태`, apart from `feature`
(`기능`) and `readiness` (`준비 상태`). `Status unavailable` becomes
`상태를 알 수 없음`, because the state could not be read, not because
the permission is unavailable.

One template changes shape: the unselected branch of
`summaryFilterAria` drops `.toLowerCase()` and reads
`${label} 권한만 표시, ${count}개`. Korean has no case, and the zh
branches already use `${label}` directly. The placeholders are
unchanged.

Verified with `tsc -p tsconfig.renderer.json --noEmit` in
`apps/desktop` before and after the change: the two errors above are
the only difference, and no error is added. The `ko` branch mirrors
the `en` branch line for line with the same 114 string literals, and it
references no `EN_*` constant. biome check is clean on the file. Tests
were not run.

Refs apache#3977

Generated-by: Claude Code
This catalog does not inline its locales. Each one is a module-level
constant collected into `COPY`, which closes with
`satisfies UiCatalog<ShellRemainingCopy>`. Add `koCopy` after `enCopy`,
typed the same way so a missing key fails at its declaration, and
register it as `ko` after `en`. The missing `ko` had surfaced twice: the
TS2741 on `COPY` and a TS7053 in `getShellRemainingCopy`. Both go away.

Scheduled-task toast titles keep the short `작업` where English says
`task`; their descriptions spell out `정기 작업`. The exceptions are
`enabled` and `paused`, which are shown as a title with no detail. In
Korean, `작업` on its own reads as a conversation task, so those two say
`정기 작업 켜짐` / `정기 작업 일시 중지됨`.

A few verbs are kept apart. `clear` is `지우기` because it keeps the task
and empties its history, next to `delete` as `삭제`. `trigger` is `실행`,
matching `run history` as `실행 기록`. `snooze` is `연기`, and `refresh`
is `새로고침`. `This cannot be undone.` becomes `이 동작은 되돌릴 수
없습니다.` so it does not collide with `작업` in the sentence before.

One template changes shape. `deleteTitle` drops the object particle
and reads `“${name}” 삭제할까요?`, because the particle would depend on
whether a user-chosen name ends in a consonant.

Six keys have no reader: `dailyReview.yesterday`, `followSettings`,
`unavailable`, `historyUnavailable`, `settingsUnavailable`, and
`tasks.loadFailed`. `ShellRemainingCopy` still requires them, so they
are translated too. Removing them is left out of this change.

Verified with `tsc -p tsconfig.renderer.json --noEmit` in
`apps/desktop` before and after the change: the two errors above are
the only difference, and no error is added. `koCopy` mirrors `enCopy`
key for key with the same 56 strings and does not reference `enCopy`.
biome check is clean on the file. Tests were not run.

Refs apache#3977

Generated-by: Claude Code
…nd the language selector

Add a `ko` block after `en` in `SETTINGS_PREFERENCES_COPY_BY_LOCALE`,
which closes with `satisfies UiCatalog<SettingsPreferencesCopy>`. The missing `ko` had surfaced twice: the
TS2741 on the catalog literal and a TS7053 in its getter. Both go away.

This change also touches the other three locales. apache#3977 requires Korean
to appear in the interface language selector, but `localeOptions` is
typed as `ReadonlyArray<readonly [UiLocalePreference, string]>`, not as
a record keyed by locale. The type does not require a `ko` row, so
adding a `ko` block alone would type-check and still leave Korean out of
the selector. Each of `zh-CN`, `zh-TW` and `en` therefore gets a
`['ko', …]` row before `['en', …]`: `한국어` in the two Chinese catalogs
(matching how they list `English` and each other by endonym) and
`Korean` in `en`. The `ko` catalog lists the same five options, with
`auto` as `시스템 설정 따름` and the other languages by endonym. The `en`
block grows from 325 to 327 strings: the `'ko'` code and `'Korean'`.

One template changes shape. `autoBypass` drops its
singular/plural ternary. Korean counters do not inflect, so the `ko`
value is a single template, `도메인 ${count}개가 …`.

Appearance wording is split by what it names. Where the text points at
the operating system setting it uses the macOS Korean name, `화면
모드`, with `라이트` / `다크`. Maka's own appearance setting is not in
System Settings, so its save-failure toast says `모양`. Color and icon
names are transliterated rather than translated, and descriptions that
repeat an icon's own name use the same transliteration.

Verified with `tsc -p tsconfig.renderer.json --noEmit` in
`apps/desktop` before and after the change: the two errors above are
the only difference, and no error is added. The `ko` block mirrors the
`en` block key for key with the same 327 strings and does not reference
any `EN_` constant or the `en` block. The 325 existing `en` values are
unchanged. biome check is clean on the file. Tests were not run.

Refs apache#3977

Generated-by: Claude Code
Add a `ko` block after `en` in `SHELL_COPY_BY_LOCALE`, which closes with
`satisfies UiCatalog<ShellCopy>`. The missing `ko` had surfaced twice:
the TS2741 on the catalog literal and a TS7053 in `getShellCopy`. Both
go away.

This catalog does not keep all of its copy inline. `commands` and
`settingsSections` point at module-level constants, and `zh-CN` and
`zh-TW` share one pair of them. Korean has nothing to share with, so
this adds `KO_STATIC_COMMANDS` after `EN_STATIC_COMMANDS` and
`KO_SETTINGS_SECTIONS` after `EN_SETTINGS_SECTIONS`, typed the same
way, and the `ko` block refers to those. Pointing `ko` at the `EN_`
constants would have type-checked and shown 86 English strings in the
Korean UI. `staticKeywords` stays on the shared `STATIC_COMMAND_KEYWORDS`.

The search-keyword arrays are not UI copy. The Korean ones keep the
English keywords and use Korean in place of the Chinese ones, as
`zh-TW` does with its own script, so each array keeps its length.

One template changes shape. `deletedSubtaskNote` drops its
singular/plural ternary, as the `zh-CN` and `zh-TW` blocks already do,
because Korean counters do not inflect. The `ko` block therefore has one
string fewer than `en`: 523 against 524.

Where a user-provided value would be followed by a particle whose form
depends on its final consonant, the sentence is reordered so that no
such particle follows the value. Affected: `openPath`, `saveSummary`,
`deleteTitle`, `deleteRestoredTitle`, `deletedDescription`, and
`planModeExitPendingDescription`.

Some wording choices:

- The phrases shared with the core `redaction.ts` catalog (`인증 실패`,
  `네트워크 오류`, `요청 시간 초과`) are kept verbatim where the English
  uses the same words.
- The command palette group `Diagnostics` is `진단`; the diagnostics
  data itself stays `진단 정보`. `task workbar` is `작업 워크바`.
  `Command palette` is `명령 팔레트`, as in VS Code.
- `flag` follows macOS Mail (`깃발 표시` / `깃발 제거`).
- Key cap names and shortcut chords are left as printed on the keys.
- `Plan mode` and `Plan Mode` are both written `Plan Mode`.
- Requests that follow a product failure end in `…해 주세요`; requests
  to correct user input, and plain guidance, end in `…하세요`.

`app.useSkillPrompt` is inserted into the composer and reaches the
model. It is translated like the rest, but how the model handles the
Korean prefix was not verified.

Verified with `tsc -p tsconfig.renderer.json --noEmit` in
`apps/desktop` before and after the change: the two errors above are
the only difference, and no error is added. The `ko` block and the two
`KO_` constants reference no `EN_` constant. With string literals
masked, the `KO_` constants match their `EN_` counterparts token for
token, and the `ko` block matches `en` except for the removed ternary.
The 524 existing `en` strings are unchanged. biome check is clean on
the file. Tests were not run.

Refs apache#3977

Generated-by: Claude Code
…on onboarding message

The Korean copy separates two kinds of instruction. When the product
could not do something and asks the user to act again, the sentence
ends in `…해 주세요`. When the product is guiding the next step, or the
user's own input needs correcting, it ends in `…하세요`. The input case
counts as guidance because the product did its job by validating the
input. A message whose causes span both sides uses `…해 주세요`.

These rules were written down only after the shell catalog was done.
All five slice 1 catalogs were then reviewed against them. Of the 27
shell strings ending in `…하세요`, 1 was changed before the shell
commit. Of the 26 such strings in the four catalogs already committed,
this is the only one that changes.

In `hero.blocked:all_connections_retired.body`, the user has to add a
new connection because Maka removed the sign-in those connections use.
The reason for the extra work lies with the product, so the sentence now
ends `…추가해 주세요.` This also aligns the onboarding hero set: the
three `needs_*` states guide with `…하세요`, and both `blocked:*` states
ask with `…해 주세요`. `blocked:all_connections_unhealthy` already reads
`…다시 테스트해 주세요.`

Only this one string changes. biome check is clean on the file.

Refs apache#3977

Generated-by: Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant