Skip to content

Commit b126c13

Browse files
i18n(platform-objects): ship the platform objects, Setup, Studio and Account in Korean (#31588)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"} --> _Requested by **Leehom** · [project thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X5xwArPxERFNdAjYY2nKcc9)_ Fixes #30512 Thread-read: 6723260365 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:给 `@objectstack/platform-objects` 加上韩语(ko):平台对象、Studio 的元数据表单、Setup/Studio/Account 导航、系统概览仪表板和用户/组织记录页的内嵌文字,做法与已合并的 pt-BR(#31440)、ru(#31466)、de(#31511)逐文件一致。 - 为什么改:ko 是 #30512 七个语言里的最后一个,本 PR 合并即关闭该卡。目前 ko 用户在控制台里看到的 `sys_*` 标签全是英文。 - 风险与回滚:只新增 ko 文件、注册行和测试里的语言列表;已有语言一个字节都不变(其余 29 个生成文件不在 diff 里);新文字同样走 source-hash 回退。回滚即 revert 本 PR。 - 先看:`packages/platform-objects/src/apps/translations/ko.ts`(导航钉住项),`ko.source-hashes.generated.ts`(45 条与英文相同的叶子,下面逐类列出),以及被 repeater-row-properties 拒绝后改为带父级名称的 3 个 `ID` 行(见 Acceptance notes)。 - 两分钟验证:`cd packages/platform-objects && npx vitest run src/apps/translations/source-hash.test.ts src/apps/translations/repeater-row-properties.test.ts`,预期 2 个文件全部通过。 - 需要你做的:生成文件让 PR 超过 3000 行,需要席位契约复审 ACCEPT,然后请人工合并。 ## What changes - New `ko` bundles in `packages/platform-objects/src/apps/translations`: `ko.objects.generated.ts` (1704 leaves), `ko.metadata-forms.generated.ts` (1263 leaves), their provenance table `ko.source-hashes.generated.ts` (45 deliberate byte copies of `en`, listed under Acceptance notes), the hand-authored `ko.ts` (141 leaves) and `ko.source-hashes.ts` recording the `en` digest of every hand leaf. - Registration: the extract config's `--locales` docstring and `translations:` baseline, `setup.translation.ts`, the `apps` barrel, and `metadata-translations/index.ts`. - Inline maps: 36 in `sys-user.page.ts`, 6 in `sys-organization.page.ts`, and the System Overview null label in `system.datasets.ts` (`시스템`). - Wording pins, as #31233 lists them: `nav_account_approvals` reads as plugin-approvals' `my_pending` label (`내 승인 대기`), `nav_assignment_rules` as `sys_assignment_rule`'s plural label (`배정 규칙`), each `nav_settings_*` (including `nav_settings_access_policy`, `액세스 정책`) as its service-settings hub title, `nav_settings_hub` as the console's `backToHub` (`전체 설정`), and the four #30935 keys: `grp_account_ai_assistant` = `AI 어시스턴트`; both `nav_connect_agent` entries and `pages.connect_agent.label` = `AI 어시스턴트 연결`. - Changeset `.changeset/30512-platform-objects-ko.md` (patch). ## Tests - `ko` added beside `de` to the locale lists of every test #31511 widened (vocabulary, nav parity, dead-key tombstone, source-hash, bu-rename with `old: /부서/`, the five identity copy tests, record-page tabs, null label, per-user stores, plugin translation loads), plus plugin-approvals `approval-status-vocabulary`, plugin-security `people-org-assignment-nav-contribution` and service-settings `setup-nav-hub-parity`. The cli `platform-page-i18n-parity` LOCALE_KEY shape already accepts `ko`. - No new negative pin, for the same reason as #31466 and #31511: the plugin-audit twin test is outside this claim's file surface. ## Acceptance notes - Every leaf is translated. The 45 byte copies of `en` the extract records fall into three groups: - Identifiers and protocol names (34): `ID` ×24, `API` ×2, `UI` ×2 (the `source` option of the two settings audit objects), `JWKS`, `JWKS URI`, `Message-ID`, `IdP SSO URL`, `snake_case`, and the SAML `urn:…:emailAddress` NameID format. - Lowercase option values and placeholder defaults that are identifiers (6): `cron`, `dlq`, `api`, and the `openid email profile` / `email` / `name` defaults. - The five JSON examples (5), kept verbatim as every locale keeps them. - `repeater-row-properties` refused three `ID` echoes in repeater rows, so each now names its parent: `영역 ID` (app areas), `노드 ID` (flow nodes) and `에지 ID` (flow edges), matching how `de` and `ja-JP` name them. - Vocabulary follows the existing `ko` plugin bundles: formal 합니다 style, confirm prompts ending `~하시겠습니까?`, `사용자`, `조직`, `사업부`, `직위`, `권한 세트`, `대기열`, `공유`, `레코드`, `승인`, `에이전트`, `스킬`, `도구`, `시스템 권한` (capability), and `상사` for a person's manager so it never collides with `관리자` (admin). Hub titles are exactly as service-settings spells them (`메일 전송`, `SMS 전송`, `파일 저장소`, `AI 및 Embedder`, `데이터 수명 주기`, …). - Business units are `사업부`; `부서` appears only where the source says department (the `department` type option and the object description), which `bu-rename-consistency` allows. - Deviation, as in #31466 and #31511: no plugin-audit `ko` twin of the #28107 activity-summary test, because plugin-audit is not in the claim's file surface. ## Verification - `os i18n extract packages/platform-objects/scripts/i18n-extract.config.ts --locales=zh-CN,ja-JP,es-ES,zh-Hant,ar,fr,pt-BR,ru,de,ko --fill=default --source-hashes --out=packages/platform-objects/src/apps/translations`: a rerun regenerates the `ko` bundles byte-identical (`md5sum -c`) and leaves the 29 other generated files unchanged (none of them is in the diff). `check-i18n-bundles --filter=platform-objects`: in sync (32 bundles). - `vitest run` in platform-objects: 120 files, 1931 tests passed. plugin-approvals `approval-status-vocabulary` (6), plugin-security `people-org-assignment-nav-contribution` (5), service-settings `setup-nav-hub-parity` plus `translations` (158) and cli `platform-page-i18n-parity` (27) passed. - platform-objects `typecheck`: exit 0. `check:app-nav-i18n`: OK (13 contributors, 11 locales, 2 apps). - `dispatch-gates --ran` at e56cfa3 (on `origin/main` 8c75f83): 68 derived families, 68 run, 0 NOT-MEASURED, all exit 0 (including `check:i18n`, `check:i18n-stale-fill`, `check:dts-closure`, `check:dual-build-cjs-loads`, `check:nul-bytes`). - `origin/main` moved 2 commits past the base (2c6b936, neither touches platform-objects, plugin-approvals or service-settings). - `eslint` over the changed `.ts` paths (scoped, `--no-inline-config`): exit 0. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01FEkQ5mqoYjnPaGZhS8jzcF)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 13f9e8e commit b126c13

36 files changed

Lines changed: 7334 additions & 48 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/platform-objects': patch
3+
---
4+
5+
i18n(platform-objects): the platform objects, Setup, Studio and Account ship in Korean
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`@objectstack/platform-objects` now carries `ko` beside `en`, `zh-CN`, `ja-JP`, `es-ES`, `zh-Hant`, `ar`, `fr`, `pt-BR`, `ru` and `de`: every `sys_*` object (labels, fields, help, options, views, actions and their dialogs), the metadata-type configuration forms Studio opens, the Setup, Studio and Account navigation, the System Overview dashboard and its datasets, the platform pages' titles, and the inline copy on the user and organization record pages. A user whose locale is `ko` no longer reads English `sys_*` labels in the console. The new bundles pass through the same source-hash fallback as the existing ones, and every hand-authored leaf records the digest of the source it was translated from. The extract config documents `ko` in its `--locales`, so `check:i18n` now gates the new bundles. Package 11 of #30512, the last of its seven locales.

‎packages/platform-objects/scripts/i18n-extract.config.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* pointed at this file via `bundle-require`:
99
*
1010
* os i18n extract packages/platform-objects/scripts/i18n-extract.config.ts \
11-
* --locales=zh-CN,ja-JP,es-ES,zh-Hant,ar,fr,pt-BR,ru,de \
11+
* --locales=zh-CN,ja-JP,es-ES,zh-Hant,ar,fr,pt-BR,ru,de,ko \
1212
* --fill=default \
1313
* --source-hashes \
1414
* --out=packages/platform-objects/src/apps/translations
@@ -228,6 +228,7 @@ import { frObjects } from '../src/apps/translations/fr.objects.generated.js';
228228
import { ptBRObjects } from '../src/apps/translations/pt-BR.objects.generated.js';
229229
import { ruObjects } from '../src/apps/translations/ru.objects.generated.js';
230230
import { deObjects } from '../src/apps/translations/de.objects.generated.js';
231+
import { koObjects } from '../src/apps/translations/ko.objects.generated.js';
231232
import { enMetadataForms } from '../src/apps/translations/en.metadata-forms.generated.js';
232233
import { zhCNMetadataForms } from '../src/apps/translations/zh-CN.metadata-forms.generated.js';
233234
import { jaJPMetadataForms } from '../src/apps/translations/ja-JP.metadata-forms.generated.js';
@@ -238,6 +239,7 @@ import { frMetadataForms } from '../src/apps/translations/fr.metadata-forms.gene
238239
import { ptBRMetadataForms } from '../src/apps/translations/pt-BR.metadata-forms.generated.js';
239240
import { ruMetadataForms } from '../src/apps/translations/ru.metadata-forms.generated.js';
240241
import { deMetadataForms } from '../src/apps/translations/de.metadata-forms.generated.js';
242+
import { koMetadataForms } from '../src/apps/translations/ko.metadata-forms.generated.js';
241243

242244
// ── Existing hand-authored app/dashboard/page translations ────────────────
243245
// The per-locale assemblers, NOT the `SetupAppTranslations` wrapper. Each one
@@ -266,6 +268,7 @@ import { fr } from '../src/apps/translations/fr.js';
266268
import { ptBR } from '../src/apps/translations/pt-BR.js';
267269
import { ru } from '../src/apps/translations/ru.js';
268270
import { de } from '../src/apps/translations/de.js';
271+
import { ko } from '../src/apps/translations/ko.js';
269272

270273
/**
271274
* The annotation is load-bearing, not decoration (#10868). `defineStack`
@@ -373,6 +376,7 @@ const config: ObjectStackDefinition = defineStack({
373376
{ 'pt-BR': { ...ptBR, objects: ptBRObjects, metadataForms: ptBRMetadataForms } },
374377
{ ru: { ...ru, objects: ruObjects, metadataForms: ruMetadataForms } },
375378
{ de: { ...de, objects: deObjects, metadataForms: deMetadataForms } },
379+
{ ko: { ...ko, objects: koObjects, metadataForms: koMetadataForms } },
376380
],
377381
});
378382

‎packages/platform-objects/src/apps/dashboards/system-overview-null-label.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('System Overview: the null-user bucket reads "System"', () => {
3636
'pt-BR': 'Sistema',
3737
ru: 'Система',
3838
de: 'System',
39+
ko: '시스템',
3940
});
4041
});
4142

‎packages/platform-objects/src/apps/dashboards/system.datasets.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export const SysAuditLogDataset = defineDataset({
5757
label: 'User',
5858
field: 'user_id',
5959
type: 'lookup',
60-
nullLabel: { en: 'System', 'zh-CN': '系统', 'ja-JP': 'システム', 'es-ES': 'Sistema', 'zh-Hant': '系統', ar: 'النظام', fr: 'Système', 'pt-BR': 'Sistema', ru: 'Система', de: 'System' },
60+
nullLabel: { en: 'System', 'zh-CN': '系统', 'ja-JP': 'システム', 'es-ES': 'Sistema', 'zh-Hant': '系統', ar: 'النظام', fr: 'Système', 'pt-BR': 'Sistema', ru: 'Система', de: 'System', ko: '시스템' },
6161
},
6262
],
6363
measures: [{ name: 'event_count', label: 'Events', aggregate: 'count' }],

‎packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { fr } from './translations/fr.js';
5151
import { ptBR } from './translations/pt-BR.js';
5252
import { ru } from './translations/ru.js';
5353
import { de } from './translations/de.js';
54+
import { ko } from './translations/ko.js';
5455
import { enObjects } from './translations/en.objects.generated.js';
5556
import { zhCNObjects } from './translations/zh-CN.objects.generated.js';
5657
import { jaJPObjects } from './translations/ja-JP.objects.generated.js';
@@ -61,6 +62,7 @@ import { frObjects } from './translations/fr.objects.generated.js';
6162
import { ptBRObjects } from './translations/pt-BR.objects.generated.js';
6263
import { ruObjects } from './translations/ru.objects.generated.js';
6364
import { deObjects } from './translations/de.objects.generated.js';
65+
import { koObjects } from './translations/ko.objects.generated.js';
6466
import { enMetadataForms } from './translations/en.metadata-forms.generated.js';
6567
import { zhCNMetadataForms } from './translations/zh-CN.metadata-forms.generated.js';
6668
import { jaJPMetadataForms } from './translations/ja-JP.metadata-forms.generated.js';
@@ -71,6 +73,7 @@ import { frMetadataForms } from './translations/fr.metadata-forms.generated.js';
7173
import { ptBRMetadataForms } from './translations/pt-BR.metadata-forms.generated.js';
7274
import { ruMetadataForms } from './translations/ru.metadata-forms.generated.js';
7375
import { deMetadataForms } from './translations/de.metadata-forms.generated.js';
76+
import { koMetadataForms } from './translations/ko.metadata-forms.generated.js';
7477

7578
/** The design-record vocabulary an admin-facing string must not carry. */
7679
const DESIGN_RECORD_VOCABULARY: ReadonlyArray<readonly [string, RegExp]> = [
@@ -145,9 +148,9 @@ function objectSchemas(...modules: Array<Record<string, unknown>>): Map<string,
145148
return byName;
146149
}
147150

148-
const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar, fr, 'pt-BR': ptBR, ru, de } as const;
151+
const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar, fr, 'pt-BR': ptBR, ru, de, ko } as const;
149152
const OBJECT_BUNDLES = {
150-
en: enObjects, 'zh-CN': zhCNObjects, 'ja-JP': jaJPObjects, 'es-ES': esESObjects, 'zh-Hant': zhHantObjects, ar: arObjects, fr: frObjects, 'pt-BR': ptBRObjects, ru: ruObjects, de: deObjects,
153+
en: enObjects, 'zh-CN': zhCNObjects, 'ja-JP': jaJPObjects, 'es-ES': esESObjects, 'zh-Hant': zhHantObjects, ar: arObjects, fr: frObjects, 'pt-BR': ptBRObjects, ru: ruObjects, de: deObjects, ko: koObjects,
151154
} as const;
152155
const FORM_BUNDLES = {
153156
en: enMetadataForms, 'zh-CN': zhCNMetadataForms, 'ja-JP': jaJPMetadataForms, 'es-ES': esESMetadataForms,
@@ -157,6 +160,7 @@ const FORM_BUNDLES = {
157160
'pt-BR': ptBRMetadataForms,
158161
ru: ruMetadataForms,
159162
de: deMetadataForms,
163+
ko: koMetadataForms,
160164
} as const;
161165

162166
describe('Setup copy carries no design-record vocabulary', () => {

‎packages/platform-objects/src/apps/setup-nav-distinct-labels.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import { fr } from './translations/fr.js';
2121
import { ptBR } from './translations/pt-BR.js';
2222
import { ru } from './translations/ru.js';
2323
import { de } from './translations/de.js';
24+
import { ko } from './translations/ko.js';
2425

25-
const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar, fr, 'pt-BR': ptBR, ru, de } as const;
26+
const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar, fr, 'pt-BR': ptBR, ru, de, ko } as const;
2627

2728
type NavItem = { id?: string; label?: string };
2829

‎packages/platform-objects/src/apps/setup-nav-per-user-stores.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,10 @@ describe('(2) the User page lists each per-user store, keyed to its record (#238
116116
'pt-BR': 'Preferências',
117117
ru: 'Предпочтения',
118118
de: 'Präferenzen',
119+
ko: '기본 설정',
119120
});
120121
expect((tabOf('sys_account')?.label as Rec)?.en).toBe('Linked Accounts');
121-
expect(Object.keys((tabOf('sys_account')?.label as Rec) ?? {}).sort()).toEqual(['ar', 'de', 'en', 'es-ES', 'fr', 'ja-JP', 'pt-BR', 'ru', 'zh-CN', 'zh-Hant']);
122+
expect(Object.keys((tabOf('sys_account')?.label as Rec) ?? {}).sort()).toEqual(['ar', 'de', 'en', 'es-ES', 'fr', 'ja-JP', 'ko', 'pt-BR', 'ru', 'zh-CN', 'zh-Hant']);
122123
});
123124
});
124125

‎packages/platform-objects/src/apps/translations/app-nav-translation-parity.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ import { fr } from './fr.js';
6464
import { ptBR } from './pt-BR.js';
6565
import { ru } from './ru.js';
6666
import { de } from './de.js';
67+
import { ko } from './ko.js';
6768

68-
const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar, fr, 'pt-BR': ptBR, ru, de } as const;
69+
const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES, 'zh-Hant': zhHant, ar, fr, 'pt-BR': ptBR, ru, de, ko } as const;
6970

7071
/** Every statically declared nav item of an app, depth-first, with its label. */
7172
function navItems(app: { navigation?: unknown[] }): Array<{ id: string; label?: string }> {

‎packages/platform-objects/src/apps/translations/bu-rename-consistency.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { frObjects } from './fr.objects.generated.js';
2020
import { ptBRObjects } from './pt-BR.objects.generated.js';
2121
import { ruObjects } from './ru.objects.generated.js';
2222
import { deObjects } from './de.objects.generated.js';
23+
import { koObjects } from './ko.objects.generated.js';
2324

2425
const LOCALES = [
2526
{ name: 'en', objs: enObjects as Record<string, any>, old: /\bDepartment\b/ },
@@ -32,6 +33,7 @@ const LOCALES = [
3233
{ name: 'pt-BR', objs: ptBRObjects as Record<string, any>, old: /Departamento/ },
3334
{ name: 'ru', objs: ruObjects as Record<string, any>, old: /Отдел/ },
3435
{ name: 'de', objs: deObjects as Record<string, any>, old: /Abteilung/ },
36+
{ name: 'ko', objs: koObjects as Record<string, any>, old: /부서/ },
3537
];
3638
const OBJECTS = ['sys_business_unit', 'sys_business_unit_member'];
3739

‎packages/platform-objects/src/apps/translations/confirm-question-carryover.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { frObjects } from './fr.objects.generated.js';
3131
import { ptBRObjects } from './pt-BR.objects.generated.js';
3232
import { ruObjects } from './ru.objects.generated.js';
3333
import { deObjects } from './de.objects.generated.js';
34+
import { koObjects } from './ko.objects.generated.js';
3435

3536
const LOCALES = [
3637
['zh-CN', zhCNObjects],
@@ -42,6 +43,7 @@ const LOCALES = [
4243
['pt-BR', ptBRObjects],
4344
['ru', ruObjects],
4445
['de', deObjects],
46+
['ko', koObjects],
4547
] as const;
4648

4749
/**

0 commit comments

Comments
 (0)