From 1d9332d8a79557196c1f412184429cbf423e07aa Mon Sep 17 00:00:00 2001 From: heeeione Date: Thu, 17 Sep 2026 14:00:34 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat(i18n-ko):=20desktop=20=E2=80=94=20add?= =?UTF-8?q?=20ko=20to=20the=20onboarding=20copy=20catalog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ko` joined `UI_LOCALES` in the core layer, and `ONBOARDING_COPY_BY_LOCALE` is annotated `UiCatalog`, 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 #3977 Generated-by: Claude Code --- .../src/renderer/locales/onboarding-copy.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/apps/desktop/src/renderer/locales/onboarding-copy.ts b/apps/desktop/src/renderer/locales/onboarding-copy.ts index f408b0ab3b..99df2a1e2c 100644 --- a/apps/desktop/src/renderer/locales/onboarding-copy.ts +++ b/apps/desktop/src/renderer/locales/onboarding-copy.ts @@ -198,6 +198,55 @@ const ONBOARDING_COPY_BY_LOCALE: UiCatalog = { skip: 'Skip onboarding', snapshotErrorFallback: 'First-run status is temporarily unavailable. Try again later.', }, + ko: { + hero: { + needs_connection: { + eyebrow: 'Maka에 오신 것을 환영합니다', + title: 'AI를 연결하고 첫 작업을 시작하세요.', + body: 'Maka는 본인의 모델 계정이나 API 키로 로컬에서 실행됩니다. 계속하려면 주요 모델 제공자를 선택하세요.', + cta: { label: '전체 모델 제공자 보기' }, + }, + needs_connection_credentials: { + eyebrow: '연결 설정 계속하기', + title: '이 연결에 사용할 인증 정보를 추가하세요.', + body: 'API 키를 추가하거나 계정 로그인을 완료하면 Maka가 모델을 호출할 수 있습니다.', + cta: { label: '인증 정보 설정' }, + }, + needs_model: { + eyebrow: '연결 설정 계속하기', + title: '이 연결에서 사용 가능한 모델을 켜세요.', + body: '대화가 가능한 모델을 켜면 첫 작업을 시작할 수 있습니다.', + cta: { label: '사용 가능한 모델 선택' }, + }, + 'blocked:all_connections_unhealthy': { + eyebrow: '연결 확인 필요', + title: '모델 연결을 일시적으로 사용할 수 없습니다.', + body: '기존 연결 중 검증을 통과한 것이 없습니다. 인증 정보, 로그인 상태, 네트워크 접근을 확인한 뒤 다시 테스트해 주세요.', + cta: { label: '모델 연결 문제 해결' }, + tone: 'destructive', + }, + 'blocked:all_connections_retired': { + eyebrow: '연결 확인 필요', + title: '연결에서 사용하는 로그인 방식의 지원이 종료되었습니다.', + body: '이 연결들이 사용하는 로그인 방식이 Maka에서 제거되었습니다. 더 이상 로그인하거나 대화에 사용할 수 없습니다. 계속하려면 새 모델 연결을 추가하세요.', + cta: { label: '모델 연결 추가' }, + tone: 'destructive', + }, + }, + needsConnection: { + pickLabel: '주요 모델 제공자 선택', + browseProviders: '전체 모델 제공자 보기', + }, + refresh: { + connection: '연결 다시 확인', + credentials: '인증 정보 다시 확인', + model: '모델 다시 확인', + blocked: '연결 다시 확인', + }, + connectionLabel: '연결', + skip: '온보딩 건너뛰기', + snapshotErrorFallback: '첫 실행 상태를 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.', + }, }; export function getOnboardingCopy(locale: UiLocale): OnboardingCatalog { From d454417b9434f827c276cf4a387029354f023ecc Mon Sep 17 00:00:00 2001 From: heeeione Date: Thu, 17 Sep 2026 14:00:34 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat(i18n-ko):=20desktop=20=E2=80=94=20add?= =?UTF-8?q?=20ko=20to=20the=20permission=20center=20copy=20catalog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the Korean branch to `PERMISSION_CENTER_COPY` after `en`. The catalog closes with `satisfies UiCatalog`, 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 #3977 Generated-by: Claude Code --- .../locales/permission-center-copy.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/apps/desktop/src/renderer/locales/permission-center-copy.ts b/apps/desktop/src/renderer/locales/permission-center-copy.ts index e2398a71eb..e868b9bf0d 100644 --- a/apps/desktop/src/renderer/locales/permission-center-copy.ts +++ b/apps/desktop/src/renderer/locales/permission-center-copy.ts @@ -272,6 +272,63 @@ const PERMISSION_CENTER_COPY = { } satisfies Record)[health], reasonFallback: 'See the runtime logs for details.', }, + ko: { + readiness: { + not_configured: { label: '설정 필요', detail: '먼저 기능을 켜거나 구성을 완료하세요.', tone: 'neutral' }, + denied: { label: '시스템에서 거부됨', detail: '필요한 시스템 권한이 거부되었거나 이 플랫폼에서 지원되지 않습니다.', tone: 'error' }, + enabled: { label: '사용 가능', detail: '현재 스냅샷은 사용 가능합니다. 자세한 내용은 아래 계층에서 확인하세요.', tone: 'success' }, + degraded: { label: '부분 사용 가능', detail: '일부 기능은 사용할 수 있지만 런타임, 권한, 하위 기능에 남은 작업이 있습니다.', tone: 'attention' }, + paused: { label: '일시 중지됨', detail: '구성은 저장된 채로 기능만 명시적으로 꺼졌습니다.', tone: 'neutral' }, + }, + osPermissions: { + accessibility: { label: '손쉬운 사용', purpose: 'Computer Use가 창 포커스를 읽고 키보드나 마우스 입력을 시뮬레이션하려면 필요합니다.', impact: 'Computer Use · 키보드와 마우스 자동 입력' }, + screen_recording: { label: '화면 및 시스템 오디오 녹음', purpose: 'Computer Use가 창 내용을 읽으려면 필요합니다. 앞으로 화면 활동 기록에도 사용됩니다.', impact: 'Computer Use · 스크린샷 컨텍스트' }, + notifications: { label: '알림', purpose: '권한 요청, 데일리 리뷰 완료 등 시스템 알림에 필요합니다.', impact: '권한 알림 · 데일리 리뷰 완료' }, + automation: { label: '자동화(Apple Events)', purpose: 'Computer Use가 다른 앱을 제어하려면 대상별 허용이 필요합니다.', impact: 'Computer Use · 앱 간 자동화' }, + }, + osStates: { + unsupported: { label: '이 플랫폼 미지원', tone: 'neutral' }, unknown: { label: '상태를 알 수 없음', tone: 'neutral' }, + not_determined: { label: '권한 대기 중', tone: 'attention' }, denied: { label: '거부됨', tone: 'error' }, granted: { label: '허용됨', tone: 'success' }, + }, + loading: '권한 스냅샷 불러오는 중', readFailed: '권한 스냅샷 읽기 실패', noData: '권한 서비스가 데이터를 반환하지 않았습니다.', readAgain: '다시 읽기', + actionFailed: '권한 동작 실패', + actionFailures: { + invalid_id: '내부 오류: 권한 ID를 인식할 수 없습니다.', + unsupported_platform: '이 운영체제는 권한 동작을 지원하지 않습니다.', + unsupported_permission: '이 플랫폼은 해당 권한의 직접 진입점을 제공하지 않습니다.', + denied: '권한이 허용되지 않았습니다. 시스템 설정에서 켤 수 있습니다.', + already_open: '다른 권한 안내가 아직 열려 있습니다. 먼저 완료하거나 닫아 주세요.', + open_settings_failed: '시스템 설정을 열지 못했습니다. 개인정보 보호 및 보안을 직접 열어 주세요.', + failed: '권한 동작이 성공하지 못했습니다. 잠시 후 다시 시도해 주세요.', + }, + title: '권한과 기능 상태', subtitle: 'Maka에 필요한 시스템 권한과 현재 상태를 확인하세요. 해당하는 개인정보 보호 및 보안 섹션을 바로 열어 접근을 허용하거나 철회하세요.', + lastRead: '최근 읽음: ', detectAgain: '다시 확인', summaryAria: '허용 상태로 시스템 권한 필터링', summaryFilterAria: (label, count, selected) => selected ? `${label}, ${count}개. 필터 적용됨. 다시 누르면 모든 권한을 표시합니다` : `${label} 권한만 표시, ${count}개`, granted: '허용됨', pending: '대기 중', denied: '거부됨', other: '알 수 없음 / 미지원', + osSection: '시스템 권한', osSectionHelp: 'Maka가 읽은 OS 수준 권한 상태입니다. 오른쪽 동작으로 시스템 설정의 해당 개인정보 보호 및 보안 섹션을 열 수 있습니다.', osListAria: '시스템 권한 목록', + capabilitiesSection: '기능 상태', capabilitiesHelp: '각 준비 상태는 기능 토글, 구성, 시스템 권한, 런타임 프로브를 합쳐서 결정됩니다.', + capabilityListAria: '기능 상태 목록', + footnote: 'Maka는 손쉬운 사용, 자동화, 화면 및 시스템 오디오 녹음을 자동으로 허용하지 않습니다. 고위험 자동화는 건별 승인, 감사 가능, 철회 가능 상태로 유지되어야 합니다. 이 페이지는 현재 스냅샷을 읽기만 합니다. 권한 변경은 여전히 시스템 설정의 개인정보 보호 및 보안에서 해야 합니다.', + layers: { + aria: (label) => `${label} 기능 상태 상세`, feature: '기능 토글', configuration: '구성', approval: '동작 승인', memory: '메모리 쓰기', runtime: '런타임 프로브', + featureStates: { enabled: '켜짐', partial: '부분 사용 가능', disabled: '꺼짐', not_available: '사용할 수 없음' }, + configurationStates: { not_required: '구성 불필요', missing: '구성 필요', present: '구성됨' }, + approvalStates: { not_required: '승인 불필요', required_per_action: '호출마다 승인 필요', required_scoped_lease: '대상과 동작 범주별 허용', pending: '승인 대기 중', approved: '이 작업에 승인됨', denied: '이 작업에 거부됨' }, + memoryStates: { not_applicable: '메모리 쓰기 없음', disabled: '메모리 쓰기 꺼짐', draft_required: '메모리 프로토콜을 먼저 작성하세요', accepted: '메모리 쓰기 수락됨' }, + runtimeStates: { not_available: '런타임 프로브 없음', not_run: '프로브 미실행', healthy: '프로브 통과', degraded: '프로브 저하' }, + }, + requiredPermissions: '필요한 시스템 권한', requiredPermissionsAria: (label) => `${label} 필요 시스템 권한`, + auditSection: '감사 기록', noAudit: '감사 기록 없음', auditAria: (label) => `${label} 감사 기록`, + impact: '영향 범위', opening: '여는 중…', openSettings: '시스템 설정 열기', requesting: '요청하는 중…', request: '권한 요청', dragGrant: '안내 받기', dragGranting: '여는 중…', + cuBackendStatus: (missing, health) => + 'maka-cu 아티팩트가 로컬 무결성 검사를 통과했습니다. ' + + (missing.length > 0 ? `${missing.join(', ')} 권한을 기다리는 중입니다. ` : '') + + ({ + not_available: 'maka-cu 서비스가 시작에 실패했거나, 종료됐거나, 중지되었습니다.', + degraded: 'maka-cu 서비스가 시작 중이거나 복구 중입니다.', + healthy: '동작·스크린샷 서비스가 준비됐습니다. 로컬 앱을 조작하려면 대상과 동작 범주별로 허용하세요.', + not_run: '서비스는 처음 사용할 때 시작됩니다. 로컬 앱을 조작하려면 대상과 동작 범주별로 허용하세요.', + } satisfies Record)[health], + reasonFallback: '자세한 내용은 런타임 로그를 확인하세요.', + }, } satisfies UiCatalog; export function getPermissionCenterCopy(locale: UiLocale): PermissionCenterCopy { From 920ed6532acb0b5abf708d7e6ed666b732334f48 Mon Sep 17 00:00:00 2001 From: heeeione Date: Thu, 17 Sep 2026 14:38:12 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat(i18n-ko):=20desktop=20=E2=80=94=20add?= =?UTF-8?q?=20ko=20to=20the=20shell=20remaining=20copy=20catalog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This catalog does not inline its locales. Each one is a module-level constant collected into `COPY`, which closes with `satisfies UiCatalog`. 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 #3977 Generated-by: Claude Code --- .../renderer/locales/shell-remaining-copy.ts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/apps/desktop/src/renderer/locales/shell-remaining-copy.ts b/apps/desktop/src/renderer/locales/shell-remaining-copy.ts index 17fb805908..f5fe4f92ff 100644 --- a/apps/desktop/src/renderer/locales/shell-remaining-copy.ts +++ b/apps/desktop/src/renderer/locales/shell-remaining-copy.ts @@ -251,10 +251,90 @@ const enCopy: ShellRemainingCopy = { }, }; +const koCopy: ShellRemainingCopy = { + scheduledTaskActions: { + refreshFailed: "작업 새로고침 실패", + refreshFallback: "정기 작업을 새로고침하지 못했습니다. 잠시 후 다시 시도해 주세요.", + created: "정기 작업 생성됨", + createFailed: "작업 생성 실패", + createFallback: "정기 작업을 생성하지 못했습니다. 잠시 후 다시 시도해 주세요.", + createIncognitoBlocked: + "시크릿 모드가 켜져 있는 동안에는 정기 작업을 생성할 수 없습니다.", + saved: "정기 작업 저장됨", + saveFailed: "작업 저장 실패", + saveFallback: "정기 작업을 저장하지 못했습니다. 잠시 후 다시 시도해 주세요.", + enabled: "정기 작업 켜짐", + paused: "정기 작업 일시 중지됨", + updateFailed: "작업 업데이트 실패", + updateFallback: "정기 작업을 업데이트하지 못했습니다. 잠시 후 다시 시도해 주세요.", + triggered: "정기 작업 실행됨", + triggerFailed: "작업 실행 실패", + triggerFallback: + "정기 작업을 실행하지 못했습니다. 잠시 후 다시 시도해 주세요.", + snoozed: "10분 연기됨", + snoozeFailed: "작업 연기 실패", + snoozeFallback: "정기 작업을 연기하지 못했습니다. 잠시 후 다시 시도해 주세요.", + task: "정기 작업", + clearTitle: (name) => `“${name}”의 실행 기록을 지울까요?`, + clearDescription: + "정기 작업은 그대로 유지됩니다. 최근 실행 기록과 상태만 지워집니다.", + clear: "기록 지우기", + cancel: "취소", + cleared: "실행 기록 지워짐", + clearFailed: "기록 지우기 실패", + clearFallback: + "정기 작업 기록을 지우지 못했습니다. 잠시 후 다시 시도해 주세요.", + deleteTitle: (name) => `“${name}” 삭제할까요?`, + deleteDescription: + "작업과 최근 실행 기록이 삭제됩니다. 이 동작은 되돌릴 수 없습니다.", + delete: "삭제", + deleted: "정기 작업 삭제됨", + deleteFailed: "작업 삭제 실패", + deleteFallback: "정기 작업을 삭제하지 못했습니다. 잠시 후 다시 시도해 주세요.", + }, + dailyReview: { + yesterday: "어제", + today: "오늘", + followSettings: "설정 따름", + unavailable: "데일리 리뷰를 생성할 수 없음", + historyUnavailable: "데일리 리뷰 기록을 사용할 수 없음", + archiveMissing: "데일리 리뷰 보고서를 찾을 수 없음", + settingsUnavailable: "데일리 리뷰 설정을 사용할 수 없음", + }, + connections: { + refreshFailed: "모델 연결 새로고침 실패", + refreshFallback: + "모델 연결을 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.", + }, + tasks: { loadFailed: "할 일 목록을 불러오지 못했습니다. 다시 시도해 주세요." }, + projects: { ungrouped: "프로젝트 없음" }, + models: { unavailable: "현재 사용할 수 없음" }, + overlays: { + loadingSettings: "설정 불러오는 중", + loadingSettingsProgress: "설정 불러오는 중…", + }, + notifications: { + scheduledTask: "정기 작업", + viewScheduledTasks: "정기 작업 보기", + }, + previousMainProcessInterruption: { + title: "Maka 복구됨", + description: "이전 종료가 완료되지 않았습니다.", + copyDiagnostics: "보고서 복사", + }, + conversationExport: { + exported: (date) => `${date}에 Maka에서 내보냈습니다.`, + you: "나", + toolCalls: "도구 호출", + intentSeparator: " — ", + }, +}; + const COPY = { 'zh-CN': zhCopy, 'zh-TW': zhTwCopy, en: enCopy, + ko: koCopy, } satisfies UiCatalog; export function getShellRemainingCopy(locale: UiLocale): ShellRemainingCopy { From ffbab36be07bd579b13a96e56418ac2daf9957af Mon Sep 17 00:00:00 2001 From: heeeione Date: Thu, 17 Sep 2026 17:30:31 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat(i18n-ko):=20desktop=20=E2=80=94=20add?= =?UTF-8?q?=20ko=20to=20the=20settings=20preferences=20catalog=20and=20the?= =?UTF-8?q?=20language=20selector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a `ko` block after `en` in `SETTINGS_PREFERENCES_COPY_BY_LOCALE`, which closes with `satisfies UiCatalog`. 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. #3977 requires Korean to appear in the interface language selector, but `localeOptions` is typed as `ReadonlyArray`, 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 #3977 Generated-by: Claude Code --- .../locales/settings-preferences-copy.ts | 68 ++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/renderer/locales/settings-preferences-copy.ts b/apps/desktop/src/renderer/locales/settings-preferences-copy.ts index a065bc83d3..cbdf97c306 100644 --- a/apps/desktop/src/renderer/locales/settings-preferences-copy.ts +++ b/apps/desktop/src/renderer/locales/settings-preferences-copy.ts @@ -270,7 +270,7 @@ const SETTINGS_PREFERENCES_COPY_BY_LOCALE = { 'zh-CN': { personalization: { saveFailed: '保存失败', displayName: '显示名称', displayNameHelp: 'Maka 在聊天里会以这个名字称呼你。留空就用默认的“你”。', displayNamePlaceholder: '例如:JK', displayNameUnset: '未设置,Maka 会称呼你“你”', displayNameChange: '更改', displayNameSet: '设置', - interfaceLanguage: '界面语言', interfaceLanguageHelp: '选择 Maka 界面的显示语言。切换后立即生效,重启后保持。', localeOptions: [['auto', '跟随系统'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], ['en', 'English']], + interfaceLanguage: '界面语言', interfaceLanguageHelp: '选择 Maka 界面的显示语言。切换后立即生效,重启后保持。', localeOptions: [['auto', '跟随系统'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], ['ko', '한국어'], ['en', 'English']], assistantTone: '助手语气偏好', assistantToneHelp: '最多 500 字,只影响回答的语气和风格。权限确认与安全规则不受影响;改动会自动保存。', assistantTonePlaceholder: '例如:技术严谨、偏简洁、不要 emoji。', }, sections: { @@ -369,7 +369,7 @@ const SETTINGS_PREFERENCES_COPY_BY_LOCALE = { 'zh-TW': { personalization: { saveFailed: '儲存失敗', displayName: '顯示名稱', displayNameHelp: 'Maka 在聊天裡會以這個名字稱呼你。留空就用預設的“你”。', displayNamePlaceholder: '例如:JK', displayNameUnset: '未設定,Maka 會稱呼你“你”', displayNameChange: '更改', displayNameSet: '設定', - interfaceLanguage: '介面語言', interfaceLanguageHelp: '選擇 Maka 介面的顯示語言。切換後立即生效,重新啟動後仍會保留。', localeOptions: [['auto', '自動(跟隨系統)'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], ['en', 'English']], + interfaceLanguage: '介面語言', interfaceLanguageHelp: '選擇 Maka 介面的顯示語言。切換後立即生效,重新啟動後仍會保留。', localeOptions: [['auto', '自動(跟隨系統)'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], ['ko', '한국어'], ['en', 'English']], assistantTone: '助手語氣偏好', assistantToneHelp: '最多 500 字,只影響回答的語氣和風格。權限確認與安全規則不受影響;改動會自動儲存。', assistantTonePlaceholder: '例如:技術嚴謹、偏簡潔、不要 emoji。', }, sections: { @@ -459,7 +459,7 @@ const SETTINGS_PREFERENCES_COPY_BY_LOCALE = { }, en: { personalization: { - saveFailed: 'Could not save', displayName: 'Display name', displayNameHelp: 'Maka uses this name when addressing you. Leave it blank to use “you”.', displayNamePlaceholder: 'For example: JK', displayNameUnset: 'Not set — Maka will say “you”', displayNameChange: 'Change', displayNameSet: 'Set', interfaceLanguage: 'Interface language', interfaceLanguageHelp: 'Choose the language used by Maka. Changes apply immediately and persist after restart.', localeOptions: [['auto', 'Follow system'], ['zh-CN', 'Simplified Chinese'], ['zh-TW', 'Traditional Chinese'], ['en', 'English']], assistantTone: 'Assistant tone', assistantToneHelp: 'Up to 500 characters. This changes response style only; permission and safety rules still apply. Changes save automatically.', assistantTonePlaceholder: 'For example: technically rigorous, concise, and no emoji.', + saveFailed: 'Could not save', displayName: 'Display name', displayNameHelp: 'Maka uses this name when addressing you. Leave it blank to use “you”.', displayNamePlaceholder: 'For example: JK', displayNameUnset: 'Not set — Maka will say “you”', displayNameChange: 'Change', displayNameSet: 'Set', interfaceLanguage: 'Interface language', interfaceLanguageHelp: 'Choose the language used by Maka. Changes apply immediately and persist after restart.', localeOptions: [['auto', 'Follow system'], ['zh-CN', 'Simplified Chinese'], ['zh-TW', 'Traditional Chinese'], ['ko', 'Korean'], ['en', 'English']], assistantTone: 'Assistant tone', assistantToneHelp: 'Up to 500 characters. This changes response style only; permission and safety rules still apply. Changes save automatically.', assistantTonePlaceholder: 'For example: technically rigorous, concise, and no emoji.', }, sections: { identity: 'Identity', identityHelp: 'How Maka addresses you, plus interface language and response tone.', @@ -519,6 +519,68 @@ const SETTINGS_PREFERENCES_COPY_BY_LOCALE = { }, password: { copyFailed: 'Copy failed', clipboardUnavailable: 'The clipboard is unavailable or access was denied.', copying: 'Copying', copied: 'Copied', copy: 'Copy', hide: 'Hide', show: 'Show', value: 'credential value' }, }, + ko: { + personalization: { + saveFailed: '저장 실패', displayName: '표시 이름', displayNameHelp: 'Maka가 사용자를 부를 때 이 이름을 씁니다. 비워 두면 이름 없이 부릅니다.', displayNamePlaceholder: '예: JK', displayNameUnset: '설정 안 됨 — Maka가 이름 없이 부릅니다', displayNameChange: '변경', displayNameSet: '설정', interfaceLanguage: '인터페이스 언어', interfaceLanguageHelp: 'Maka에서 사용할 언어를 선택하세요. 변경 사항은 즉시 적용되며 다시 시작한 후에도 유지됩니다.', localeOptions: [['auto', '시스템 설정 따름'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], ['ko', '한국어'], ['en', 'English']], assistantTone: '어시스턴트 어조', assistantToneHelp: '최대 500자입니다. 응답 스타일만 바뀌며 권한과 안전 규칙은 그대로 적용됩니다. 변경 사항은 자동으로 저장됩니다.', assistantTonePlaceholder: '예: 기술적으로 엄밀하게, 간결하게, 이모지 없이.', + }, + sections: { + identity: '프로필', identityHelp: 'Maka가 사용자를 부르는 방식과 인터페이스 언어, 응답 어조를 정합니다.', + privacy: '개인정보 보호 및 알림', privacyHelp: 'Maka가 로컬에서 읽고 쓸 수 있는 범위와 알림을 보내는 시점을 정합니다.', + chatDefaults: '작업 기본값', chatDefaultsHelp: '새 작업이 시작할 때 쓰는 모델, 권한 모드, 사고 수준입니다.', + shell: '명령 실행 환경', shellHelp: 'Runtime Host가 Bash 도구와 터미널 명령에 사용할 셸을 선택하세요.', + network: '네트워크', networkHelp: 'AI 모델 요청이 거치는 네트워크 경로입니다.', + theme: '테마', themeHelp: '시스템 화면 모드를 따르거나 라이트 또는 다크로 고정합니다.', + palette: '색상 팔레트', paletteHelp: '강조 색상과 캔버스 색상입니다. 변경 사항은 즉시 적용되고 로컬에 저장됩니다.', + appIcon: '앱 아이콘', appIconHelp: 'Dock, 작업 표시줄, 앱 전환기에 표시되는 Maka 아이콘입니다. 변경 사항은 즉시 적용됩니다.', + fontSize: '글꼴 크기', fontSizeHelp: '인터페이스와 터미널 전체의 텍스트 크기입니다. 변경 사항은 즉시 적용되고 로컬에 저장됩니다.', + pets: '사용자 지정 펫', petsHelp: '직접 가져온 PetPack을 관리합니다. Maka는 기본으로 포함하거나 켜 두는 펫이 없습니다.', + }, + appearance: { + saveFailed: '모양 설정 저장 실패', theme: '테마', palette: '색상 팔레트', themeOptions: { light: { label: '라이트', help: '항상 라이트 인터페이스를 사용합니다.' }, dark: { label: '다크', help: '항상 다크 인터페이스를 사용합니다.' }, auto: { label: '시스템 설정 따름', help: '현재 시스템 화면 모드에 맞춥니다.' } }, paletteLabels: { default: '기본', onedark: 'One Dark', 'catppuccin-mocha': 'Catppuccin Mocha', 'tokyo-night': 'Tokyo Night', nord: 'Nord', coral: '코랄', azure: '애저', forest: '포레스트', dusk: '더스크', sand: '샌드', mono: '모노크롬' }, paletteHelp: { default: 'Maka 브랜드 블루 강조 색상', onedark: '클래식 다크 에디터 테마', 'catppuccin-mocha': '부드러운 보라색 다크 테마', 'tokyo-night': '짙은 파란색 에디터 테마', nord: '차가운 북유럽 색감', coral: '따뜻한 분홍·코랄 강조 색상', azure: '깔끔하고 차분한 파란색 강조 색상', forest: '짙은 이끼색과 따뜻한 꿀색', dusk: '차가운 캔버스 위의 짙은 보라색', sand: '앰버 샌드와 따뜻한 아이보리', mono: '색이 없는 순수 회색조' }, paletteGroups: { editor: '에디터 테마', product: '제품 색상' }, appIconLabels: { default: '클래식', mono: '모노크롬', 'sky': '스카이', 'cyan': '시안', 'ice': '아이스', 'pale-inverted': '인버티드', 'ink': '잉크', 'paper': '페이퍼', 'graphite': '그래파이트', 'pencil-kraft': '펜슬, 크래프트', 'pencil-sky': '펜슬, 스카이', 'pencil-navy': '펜슬, 네이비', 'alpine': '알파인', 'dusk': '더스크', 'night': '나이트', 'midnight': '미드나이트', 'carbon': '카본', 'slate': '슬레이트', 'obsidian': '옵시디언', 'neon-cyan': '네온 시안', 'matrix': '포스퍼', 'magenta': '마젠타', 'amber-crt': '앰버 CRT', 'clay': '클레이', 'sage': '세이지', 'dust': '더스트', 'fog': '포그', 'sunset': '선셋', 'amber': '앰버', 'terracotta': '테라코타', 'ocean': '오션', 'moss': '모스', 'desert': '데저트', 'glacier': '글레이셔', 'gold': '골드', 'chrome': '크롬', 'mono-black': '모노 블랙', 'mono-white': '모노 화이트', 'hazard': '해저드', 'forest': '포레스트' }, appIconHelp: { default: '기본 Maka 마크', mono: '회색조, 더 차분한 Dock용', 'sky': '브랜드 블루의 기하학적 M 마크', 'cyan': '시안에 가까운 파란색', 'ice': '옅은 파란색에서 짙은 파란색으로 이어지는 그라데이션', 'pale-inverted': '옅은 바탕에 짙은 파란색 마크', 'ink': '검은 바탕에 흰색, 가장 높은 대비', 'paper': '흰 바탕에 검은색', 'graphite': '흰 바탕에 검은색, 끝은 회색', 'pencil-kraft': '펜슬 모티프, 크래프트 바탕', 'pencil-sky': '펜슬 모티프, 스카이 블루 바탕', 'pencil-navy': '펜슬 모티프, 짙은 네이비 바탕', 'alpine': '맑은 하늘 아래 눈 덮인 봉우리', 'dusk': '황혼의 눈 덮인 봉우리', 'night': '밤의 눈 덮인 봉우리', 'midnight': '짙은 네이비 바탕의 밝은 마크. 어두운 Dock에서도 윤곽이 살아 있음', 'carbon': '완전한 검정이라 OLED 화면에는 마크만 보임', 'slate': '차가운 슬레이트 바탕에 옅은 회색', 'obsidian': '보랏빛 검정 그라데이션 바탕에 라일락색', 'neon-cyan': '검정에 가까운 바탕에 선명한 시안', 'matrix': '포스퍼 터미널의 녹색', 'magenta': '짙은 보라색 바탕에 핫핑크', 'amber-crt': '초기 터미널의 앰버색', 'clay': '차분한 테라코타', 'sage': '차분한 회녹색', 'dust': '차분한 더스티 로즈', 'fog': '차분한 청회색', 'sunset': '주황에서 분홍으로 이어지는 대각선', 'amber': '앰버 바탕에 어두운 마크', 'terracotta': '벽돌색 그라데이션', 'ocean': '짙은 청록 그라데이션', 'moss': '짙은 모스 그라데이션', 'desert': '데저트 샌드 바탕에 어두운 마크', 'glacier': '옅은 빙하색 파랑', 'gold': '마크 자체에 골드 그라데이션', 'chrome': '마크 자체에 은색 그라데이션', 'mono-black': '순백 바탕에 검은색. 한 가지 색으로 인쇄 가능', 'mono-white': '완전한 검정 바탕에 흰색', 'hazard': '검은 바탕에 노란색, 이 묶음에서 대비가 가장 높음', 'forest': '녹색 바탕의 눈 덮인 봉우리' }, appIconGroups: { mascot: '마스코트', blue: '파란색 계열', contrast: '흑백', pencil: '펜슬', mountain: '산', dark: '다크', neon: '네온', muted: '차분한 색', warm: '따뜻한 색', nature: '자연', metal: '금속', highContrast: '고대비', custom: '가져온 항목' }, appIconSplitLabel: '다크 모드에서 다른 아이콘 사용', appIconSplitHelp: '끄면 두 화면 모드 모두 같은 아이콘을 사용합니다.', appIconTargets: { light: '라이트', dark: '다크' }, appIconCustom: '가져온 아이콘', appIconCustomHelp: '직접 가져온 이미지', appIconImport: '아이콘 가져오기…', appIconImporting: '가져오는 중…', appIconImportHelp: '정사각형 PNG가 가장 좋습니다. Dock에서 다른 앱과 같은 크기로 보이도록 가장자리에 약 10%의 투명 여백을 두세요.', appIconRemove: '제거', appIconImportError: '아이콘 가져오기 실패', appIconRemoveFailed: '아이콘 제거 실패', appIconSelectFailed: '아이콘 전환 실패', appIconImportFailed: { too_large: '파일이 너무 큽니다. 더 작은 이미지를 선택하세요', too_many_pixels: '이미지가 너무 큽니다. 최대 4096×4096입니다', unsupported_format: 'PNG와 JPEG만 지원됩니다', unreadable: '이 파일에서 이미지를 읽지 못했습니다', too_small: '이미지가 너무 작습니다. 최소 128×128입니다', write_failed: '가져온 아이콘 저장 실패' }, appIconUnavailable: '앱 아이콘 불러오기 실패', fontSize: { uiLabel: 'UI 글꼴 크기', uiHelp: '인터페이스 전체에 쓰이는 기본 글꼴 크기', terminalLabel: '터미널 글꼴 크기', terminalHelp: '터미널 출력과 코드에 쓰이는 글꼴 크기' }, + }, + pets: { + import: 'PetPack 가져오기', importing: '가져오는 중…', loading: '사용자 지정 펫 불러오는 중…', + status: '데스크톱 펫', activePet: (name) => `현재 사용 중: ${name}`, disabled: '꺼짐', disable: '펫 끄기', disabling: '끄는 중…', + empty: '아직 가져온 펫 없음', emptyHelp: 'pet.json과 스프라이트 시트가 들어 있는 로컬 폴더를 선택하세요.', + selected: '사용 중', select: '사용', selecting: '전환하는 중…', remove: '제거', removing: '제거하는 중…', + removeTitle: (name) => `“${name}” 제거할까요?`, removeDescription: 'Maka에 저장된 이 펫 팩의 로컬 사본이 제거되며 되돌릴 수 없습니다. 원본 폴더에는 영향이 없습니다.', confirmRemove: '제거', cancel: '취소', + loadFailed: '사용자 지정 펫 불러오기 실패', importFailed: '펫 가져오기 실패', selectFailed: '펫 전환 실패', removeFailed: '펫 제거 실패', + importErrors: { invalid_directory: '선택한 폴더가 올바르지 않습니다.', invalid_manifest: 'pet.json이 maka.pet/v1 형식과 맞지 않습니다.', invalid_asset: '스프라이트 시트가 없거나, 올바르지 않거나, 지원 범위를 벗어났습니다.', already_installed: '같은 ID의 펫이 이미 설치되어 있습니다.', read_failed: '선택한 폴더를 읽지 못했습니다.' }, + selectErrors: { invalid_id: '펫 ID가 올바르지 않습니다.', not_found: '이 펫은 더 이상 로컬 라이브러리에 없습니다.', read_failed: '펫 라이브러리를 읽지 못했습니다.', write_failed: '펫 선택을 저장하지 못했습니다.' }, + removeErrors: { invalid_id: '펫 ID가 올바르지 않습니다.', remove_failed: '로컬 펫 팩을 제거하지 못했습니다.' }, + }, + general: { + incognito: '시크릿 모드', incognitoHelp: '로컬 메모리, 웹 검색, 정기 작업 실행을 일시 중지합니다.', enableIncognito: '시크릿 모드 켜기', incognitoFailed: '시크릿 모드 변경 실패', notifications: '완료되면 시스템 알림 보내기', notificationsHelp: '창이 백그라운드에 있을 때 응답이 완료되거나 실패하면 알립니다.', notificationsFailed: '알림 설정 변경 실패', workspaceInstructions: '프로젝트 지침 따르기', workspaceInstructionsHelp: '각 프로젝트에 있는 AGENTS.md, CLAUDE.md, GEMINI.md 파일을 자동으로 읽습니다. 파일은 각 프로젝트에서 관리하세요.', workspaceInstructionsFailed: '프로젝트 지침 설정 변경 실패', workHub: 'WorkHub 켜기', workHubHelp: 'WorkHub는 아직 사용할 수 없습니다. 이 토글은 개발 테스트용이며, 켜도 사용할 수 있는 기능이 생기지 않습니다.', workHubFailed: 'WorkHub 설정 변경 실패', updateFailed: '설정이 적용되지 않았습니다. 잠시 후 다시 시도해 주세요.', defaultModel: '기본 모델', defaultModelHelp: '새 작업에 사용할 모델입니다.', notSet: '설정 안 됨', saveDefaultModelFailed: '기본 모델 저장 실패', defaultPermission: '기본 권한 모드', defaultPermissionHelp: '새 작업의 초기 권한 모드입니다. 언제든지 바꿀 수 있습니다.', saveDefaultPermissionFailed: '기본 권한 모드 저장 실패', defaultThinking: '기본 사고 수준', defaultThinkingHelp: '새 작업의 사고 수준입니다. 선택한 수준을 지원하지 않는 모델은 자체 기본값을 사용합니다.', followModelDefault: '모델 기본값 따름', saveDefaultThinkingFailed: '기본 사고 수준 저장 실패', proxy: '프록시 서버', proxyHelp: 'AI 모델 요청에 사용할 네트워크 프록시를 설정하세요', enableProxy: '프록시 서버 켜기', saveNetworkFailed: '네트워크 설정 저장 실패', proxyProtocol: '프록시 프로토콜', serverAddress: '서버 주소', port: '포트', proxyAuth: '프록시 인증', proxyAuthHelp: '사용자 이름과 비밀번호가 필요할 때 켜세요.', enableProxyAuth: '프록시 인증 켜기', username: '사용자 이름', password: '비밀번호', bypassList: '프록시 우회 목록', bypassHelp: '이 도메인은 프록시를 거치지 않고 직접 연결됩니다. 여러 도메인은 쉼표로 구분하세요.', autoBypass: (count) => `도메인 ${count}개가 자동으로 추가되었습니다. 프록시는 AI 모델 요청에만 적용됩니다.`, testing: '테스트하는 중…', testCurrent: '현재 구성 테스트', proxyReachable: '프록시에 연결할 수 있음', proxyTestFailed: '프록시 테스트 실패', proxyTestError: '프록시를 테스트하지 못함', + shellPreference: 'Bash 도구 셸', shellPreferenceHelp: '자동은 PowerShell을 우선하는 Windows 기본 동작을 유지합니다. Git Bash는 현재 Runtime Host에만 적용되는 명시적 재정의입니다.', shellAuto: '자동(권장)', shellGitBash: 'Git Bash', shellExecutable: 'Git Bash 실행 파일', shellExecutableHelp: 'Runtime Host를 실행하는 Windows 컴퓨터에서 bash.exe의 절대 경로를 입력하세요. 기존 System32 WSL Bash 심(shim)도 인식합니다. Maka는 저장하기 전에 GNU Bash인지 확인합니다.', saveShell: '셸 설정 저장', savingShell: '저장하는 중…', shellSaved: '저장됨', saveShellFailed: '셸 설정 저장 실패', shellExecutableRejected: '현재 Runtime Host에서 이 경로를 GNU Bash로 실행하지 못했습니다. Host가 Windows에서 실행 중인지, 경로가 있는지, 파일 이름이 bash.exe인지 확인하세요.', + passwordSavedPlaceholder: '비밀번호가 저장되어 있습니다. 바꾸려면 새 비밀번호를 입력하세요', + }, + about: { + loadFailed: '앱 정보 불러오기 실패', loading: '앱 정보 불러오는 중', unavailable: '앱 정보를 사용할 수 없음', copied: '진단 정보 복사됨', pasteHint: '내용을 확인한 뒤 이슈 보고서에 붙여 넣으세요', copyFailed: '복사 실패', clipboardUnavailable: '클립보드를 사용할 수 없거나 접근이 거부되었습니다.', + channelSummaries: { + dev: '로컬 개발 빌드입니다. 업데이트를 확인하지 않습니다.', + nightly: '매일 나오는 사전 릴리스 빌드입니다. 최신 nightly로 자동 업데이트되며 정식 릴리스 설치를 대체합니다.', + release: '공식 릴리스 빌드입니다. 안정 업데이트를 자동으로 받습니다.', + }, + supportTitle: '지원', + copyDiagnostics: '진단 정보 복사', copyAction: '복사', copyHelp: '버전, 플랫폼, 홈 경로를 가린 작업 공간 경로, 민감 정보를 가린 최근 로그를 복사합니다. 보고서는 클립보드에만 기록되며 자동으로 업로드되지 않습니다.', + reportIssueLabel: '이슈 보고', reportIssueHelp: '진단 정보를 첨부해 GitHub 이슈를 여세요 — 더 빨리 답변을 받을 수 있습니다.', reportIssueOpen: '열기', + keyboardShortcuts: '키보드 단축키', keyboardShortcutsHelp: 'Maka가 지원하는 모든 단축키입니다.', keyboardShortcutsOpen: '보기', + checkForUpdates: '업데이트 확인', + checkingForUpdates: '업데이트 확인하는 중…', + updateIdle: '아직 업데이트를 확인하지 않음', + updateNotAvailable: '최신 버전을 사용 중입니다', + updateAvailable: (version) => `v${version} 사용 가능`, + updateDownloading: (version, percent) => `v${version} 다운로드하는 중(${percent}%)`, + updateVerifying: (version) => `v${version}의 릴리스 출처 확인하는 중`, + updateDownloaded: (version) => `v${version} 설치 준비됨`, + updateDownloadedHint: '사이드바 아래쪽에서 다시 시작하면 설치됩니다.', + updateInstalling: (version) => `v${version} 설치하는 중`, + updateFailed: { check: '업데이트 확인 실패', download: '업데이트 다운로드 실패', install: '업데이트 설치 실패' }, + openSourceSummary: 'Apache Maka (incubating) · Apache License 2.0', + sourceCode: '소스 코드', releaseNotes: '릴리스 노트', + }, + password: { copyFailed: '복사 실패', clipboardUnavailable: '클립보드를 사용할 수 없거나 접근이 거부되었습니다.', copying: '복사하는 중', copied: '복사됨', copy: '복사', hide: '숨기기', show: '표시', value: '인증 정보 값' }, + }, } satisfies UiCatalog; export function getSettingsPreferencesCopy(locale: UiLocale): SettingsPreferencesCopy { From 805c030692f24f74eafb8f361c71c53b3b47052e Mon Sep 17 00:00:00 2001 From: heeeione Date: Thu, 17 Sep 2026 18:38:27 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat(i18n-ko):=20desktop=20=E2=80=94=20add?= =?UTF-8?q?=20ko=20to=20the=20shell=20copy=20catalog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a `ko` block after `en` in `SHELL_COPY_BY_LOCALE`, which closes with `satisfies UiCatalog`. 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 #3977 Generated-by: Claude Code --- .../src/renderer/locales/shell-copy.ts | 669 ++++++++++++++++++ 1 file changed, 669 insertions(+) diff --git a/apps/desktop/src/renderer/locales/shell-copy.ts b/apps/desktop/src/renderer/locales/shell-copy.ts index 037fcae303..a376f7a022 100644 --- a/apps/desktop/src/renderer/locales/shell-copy.ts +++ b/apps/desktop/src/renderer/locales/shell-copy.ts @@ -716,6 +716,105 @@ const EN_STATIC_COMMANDS: Record = { }, }; +const KO_STATIC_COMMANDS: Record = { + 'action:new-chat': { + label: '새 작업', + hint: '새 작업 시작', + group: '동작', + }, + 'action:side-chat': { + label: '사이드 채팅 열기', + platformHint: { apple: '⌥⌘S', other: 'Ctrl+Alt+S' }, + group: '동작', + }, + 'action:new-deep-research': { + label: '새 심층 리서치', + hint: '읽기 전용 탐색', + group: '동작', + }, + 'action:new-scheduled-task': { + label: '새 정기 작업', + hint: '작업 양식 열기', + group: '동작', + }, + 'action:open-settings': { + label: '설정 열기', + platformHint: { apple: '⌘,', other: 'Ctrl+,' }, + group: '동작', + }, + 'action:keyboard-help': { + label: '키보드 단축키 보기', + hint: '?', + group: '동작', + }, + 'theme:light': { label: '테마 · 라이트', group: '테마' }, + 'theme:dark': { label: '테마 · 다크', group: '테마' }, + 'theme:auto': { label: '테마 · 시스템 설정 따름', group: '테마' }, + 'nav:sessions': { label: '사이드바 · 작업', group: '내비게이션' }, + 'nav:automations': { label: '사이드바 · 자동화', group: '내비게이션' }, + 'nav:skills': { label: '열기 · Skills', group: '내비게이션' }, + 'nav:mcp': { label: '열기 · MCP', group: '내비게이션' }, + 'nav:daily-review': { label: '열기 · 데일리 리뷰', group: '내비게이션' }, + 'diag:open-workspace': { + label: '작업 공간 폴더 열기', + hint: 'Finder', + group: '진단', + }, + 'diag:open-project-folder': { + label: '프로젝트 폴더 열기', + hint: 'Finder', + group: '진단', + }, + 'diag:open-skills': { + label: 'Skills 폴더 열기', + hint: 'Finder', + group: '진단', + }, + 'diag:export-conversation': { + label: '작업을 Markdown으로 복사', + hint: '클립보드에 복사', + group: '진단', + }, + 'diag:save-conversation-file': { + label: '작업을 .md 파일로 저장', + hint: '시스템 저장 대화상자 사용', + group: '진단', + }, + 'diag:copy-today-daily-review': { + label: "오늘의 리뷰를 Markdown으로 복사", + hint: '클립보드에 복사', + group: '진단', + }, + 'diag:paste-today-daily-review': { + label: "오늘의 리뷰를 입력창에 붙여 넣기", + hint: '클립보드를 거치지 않음', + group: '진단', + }, + 'diag:save-today-daily-review': { + label: "오늘의 리뷰를 .md 파일로 저장", + hint: '시스템 저장 대화상자 사용', + group: '진단', + }, + 'diag:copy-diagnostics': { + label: '진단 정보 복사', + platformHint: { + apple: '⇧⌘D · 민감 정보를 가린 로그 · 클립보드에만', + other: 'Ctrl+Shift+D · 민감 정보를 가린 로그 · 클립보드에만', + }, + group: '진단', + }, + 'diag:test-network-proxy': { + label: '현재 네트워크 프록시 테스트', + hint: '설정을 열지 않고 진단', + group: '진단', + }, + 'diag:open-local-memory': { + label: '로컬 MEMORY.md 열기', + hint: '시스템 편집기', + group: '진단', + }, +}; + const ZH_SETTINGS_SECTIONS: Record = { general: '通用', appearance: '外观', @@ -754,6 +853,25 @@ const EN_SETTINGS_SECTIONS: Record = { about: 'About', }; +const KO_SETTINGS_SECTIONS: Record = { + general: '일반', + appearance: '모양', + projects: '작업 공간', + models: '모델', + subagents: '하위 에이전트', + usage: '사용량', + 'archived-tasks': '보관된 작업', + 'import-tasks': '작업 가져오기', + memory: '메모리', + 'daily-review': '데일리 리뷰', + 'bot-chat': '원격 접속', + search: '웹 검색', + data: '데이터', + permissions: '권한과 기능 상태', + health: '상태 점검', + about: '앱 정보', +}; + const SHELL_COPY_BY_LOCALE = { 'zh-CN': { navigation: { settings: '设置' }, @@ -2310,6 +2428,557 @@ const SHELL_COPY_BY_LOCALE = { resizeWorkbar: 'Resize task workbar', }, }, + ko: { + navigation: { settings: '설정' }, + actions: { retry: '다시 시도' }, + paths: { + workspace: '작업 공간', + project: '프로젝트 폴더', + skills: 'Skills 폴더', + }, + errors: { + messageRead: '작업 내용을 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.', + messageRefresh: '작업 내용을 새로고침하지 못했습니다. 잠시 후 다시 시도해 주세요.', + openPath: (path: string) => `${path} 열기에 실패했습니다. 잠시 후 다시 시도해 주세요.`, + workspaceUnavailableTitle: '작업 디렉터리를 사용할 수 없음', + workspaceUnavailableDescription: + '작업 디렉터리가 없거나 접근할 수 없습니다. 새 작업에 사용할 올바른 폴더를 선택하세요.', + }, + chatActions: { + newConversation: '새 작업', + sendFailedTitle: '메시지를 보내지 못함', + sendFailedFallback: '메시지를 보내지 못했습니다. 잠시 후 다시 시도해 주세요.', + skillInvocationBlockedTitle: 'Skill 호출에 실패해 메시지를 보내지 못함', + skillInvocationBlockedDescription: (items) => `${items.join(', ')}. 선택을 조정한 뒤 다시 시도해 주세요.`, + skillInvocationFailedTitle: '일부 Skill을 호출하지 못했습니다', + skillInvocationFailedDescription: (items) => + `${items.join(', ')}. 나머지 Skill은 호출되었습니다.`, + skillInvocationFailureReason: { + invalid_name: '잘못된 이름', + not_found: '찾을 수 없음', + disabled: '꺼짐', + host_incompatible: '필요한 도구를 사용할 수 없음', + resolution_failed: '해석 실패', + too_many_requests: '서로 다른 Skill 호출 요청이 50개를 넘음', + }, + responseFailedTitle: '응답 실패', + responseFailedFallback: '작업 동작을 수행하지 못했습니다. 잠시 후 다시 시도해 주세요.', + refreshFailedTitle: '작업 새로고침 실패', + sessionStartFailedTitle: '작업 시작 실패', + sessionStartFailedFallback: '작업을 시작하지 못했습니다. 잠시 후 다시 시도해 주세요.', + }, + projectActions: { + currentProject: '현재 프로젝트', + readPathFailedTitle: '프로젝트 경로 읽기 실패', + readPathFailedFallback: '프로젝트 경로를 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.', + selectDirectoryFailedTitle: '작업 디렉터리 선택 실패', + selectedPathUnreadable: '선택한 경로가 없거나 읽을 수 없습니다.', + directorySwitchedTitle: '작업 디렉터리 변경됨', + projectUpdateFailedTitle: '프로젝트 업데이트 실패', + projectUpdateFailedFallback: '프로젝트를 업데이트하지 못했습니다. 잠시 후 다시 시도해 주세요.', + catalogUnavailable: 'Runtime Host를 사용할 수 없음', + retryCatalog: '불러오기 다시 시도', + remoteDirectoryTitle: (host: string) => `${host}에 프로젝트 추가`, + remoteDirectoryBreadcrumbs: '현재 폴더', + remoteDirectoryHome: '홈', + remoteDirectoryEmpty: '이 위치에 폴더 없음', + remoteDirectorySelect: '이 폴더 추가', + remoteDirectoryCancel: '취소', + remoteDirectoryRetry: '다시 시도', + remoteDirectoryLoading: '폴더 불러오는 중…', + remoteDirectoryShowHidden: '숨김 폴더 표시', + remoteDirectoryHideHidden: '숨김 폴더 숨기기', + runtimeHostReadiness: { + connecting: '연결하는 중', + reconnecting: '다시 연결하는 중', + unavailable: '사용할 수 없음', + }, + openFailedTitle: (path: string) => `${path} 열기 실패`, + openPathLabels: { + workspace: '작업 공간 폴더', + skills: 'Skills 폴더', + memory: '메모리 폴더', + project: '프로젝트 폴더', + }, + openPathFailures: { + 'unknown-key': '알 수 없는 작업 공간 폴더입니다.', + 'not-allowed': '경로가 Maka에서 열 수 있는 폴더 밖에 있습니다.', + missing: '폴더가 없습니다.', + 'not-a-directory': '대상이 폴더가 아닙니다.', + 'open-failed': '시스템에서 폴더를 열지 못했습니다.', + unknown: '폴더를 열지 못했습니다.', + }, + }, + commandActions: { + connectionVerified: (name: string) => `연결 확인됨 · ${name}`, + connectionLatency: (latency: number | string, model?: string) => + `지연 시간 ${latency}ms${model ? ` · ${model}` : ''}`, + connectionTestFailed: (name: string) => `연결 테스트 실패 · ${name}`, + testErrorTitle: '테스트 오류', + connectionUnavailable: '연결 테스트를 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.', + connectionFailures: { + rateLimit: '계정 또는 모델 서비스의 속도 제한을 초과했습니다. 잠시 후 다시 시도해 주세요.', + timeout: '요청 시간 초과. 네트워크 또는 프록시를 확인한 뒤 다시 시도해 주세요.', + auth: '인증 실패. 모델 키, 구독 로그인 또는 인증 정보를 확인한 뒤 다시 시도해 주세요.', + network: '네트워크 오류. 네트워크 또는 프록시를 확인한 뒤 다시 시도해 주세요.', + provider: '모델 서비스가 오류를 반환했습니다. 잠시 후 다시 시도해 주세요.', + unknown: '연결 테스트에 실패했습니다. 잠시 후 다시 시도해 주세요.', + }, + setDefaultSuccess: (name: string) => `기본값으로 설정됨 · ${name}`, + setDefaultFailedTitle: '기본값 변경 실패', + setDefaultFallback: '기본 모델을 변경하지 못했습니다. 잠시 후 다시 시도해 주세요.', + newConversation: '새 작업', + conversationCopiedTitle: '작업이 Markdown으로 복사됨', + lineCount: (lines: number) => `${lines}줄 · Notion / Obsidian / GitHub에 바로 붙여 넣을 수 있음`, + copyFailedTitle: '복사 실패', + clipboardUnavailable: '클립보드를 사용할 수 없음', + conversationSavedTitle: '작업 저장됨', + saveSummary: (lines: number, fileName: string) => `${lines}줄 · ${fileName} 파일로 저장됨`, + saveFailedTitle: '저장 실패', + invalidExport: '내보낼 내용이 올바르지 않습니다', + writeFailed: '선택한 위치에 쓰지 못했습니다', + exportFallback: '작업을 내보내지 못했습니다. 잠시 후 다시 시도해 주세요.', + memoryOpenFailedTitle: 'MEMORY.md 열기 실패', + openFailedTitle: '열기 실패', + memoryOpenFallback: 'MEMORY.md를 열지 못했습니다. 잠시 후 다시 시도해 주세요.', + today: '오늘', + reviewCopiedTitle: "오늘의 리뷰가 Markdown으로 복사됨", + reviewSummary: (sessions: number, requests: number) => `작업 ${sessions}개 · 요청 ${requests}개`, + reviewCopyFallback: "오늘의 리뷰를 사용할 수 없거나 클립보드 접근이 거부되었습니다.", + reviewPastedTitle: "오늘의 리뷰가 입력창에 추가됨", + reviewCopied: (label: string) => `${label} 리뷰 복사됨`, + reviewPasted: (label: string) => `${label} 리뷰가 입력창에 추가됨`, + reviewSaved: (label: string) => `${label} 리뷰 저장됨`, + reviewSaveFallback: '데일리 리뷰를 저장하지 못했습니다. 잠시 후 다시 시도해 주세요.', + pasteFailedTitle: '붙여 넣기 실패', + reviewUnavailable: "오늘의 리뷰를 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.", + diagnosticsCopiedTitle: '진단 정보 복사됨', + diagnosticsCopiedDescription: '내용을 확인한 뒤 이슈 보고서에 붙여 넣으세요', + clipboardDenied: '클립보드를 사용할 수 없거나 접근이 거부되었습니다', + networkPassedTitle: '네트워크 프록시 테스트 통과', + networkFailedTitle: '네트워크 프록시 테스트 실패', + genericTestFailedTitle: '테스트 실패', + networkTestFallback: '네트워크 프록시 테스트를 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.', + }, + sessionRowActions: { + actionFallback: '작업 동작을 수행하지 못했습니다. 잠시 후 다시 시도해 주세요.', + flagFailedTitle: '작업 깃발 표시 실패', + unflagFailedTitle: '깃발 제거 실패', + archiveFailedTitle: '작업 보관 실패', + unarchiveFailedTitle: '작업 복원 실패', + renameFailedTitle: '작업 이름 변경 실패', + deleteFailedTitle: '작업 삭제 실패', + currentConversation: '현재 작업', + deleteTitle: (name: string) => `"${name}" 삭제`, + deleteDescription: + '작업과 모든 메시지가 디스크에서 영구적으로 제거됩니다. 이 동작은 되돌릴 수 없습니다.', + deleteLabel: '삭제', + cancelLabel: '취소', + deletedTitle: (name: string) => `${name} 삭제됨`, + deleteRestoredTitle: (name: string) => `${name}: 복원되어 유지됨`, + deleteSubtaskNote: () => '일반 하위 작업은 유지되며 보관된 작업으로 이동합니다.', + deleteSubtaskNoteUncertain: () => + '일반 하위 작업이 있으면 유지되며 보관된 작업으로 이동합니다.', + deletedSubtaskNote: (count: number) => + `하위 작업 ${count}개를 보관된 작업으로 이동했습니다`, + bulkArchiveDescription: '보관된 작업은 설정 › 활동에서 계속 볼 수 있습니다.', + bulkArchivedTitle: (count: number) => `${count}개 작업을 보관했습니다`, + bulkArchiveFailedTitle: '일부 작업을 보관하지 못했습니다', + bulkFailedBody: (count: number) => `그중 ${count}개는 처리되지 않았습니다.`, + }, + skillActions: { + refreshSkillsFailedTitle: 'Skills 새로고침 실패', + refreshSkillsFallback: 'Skills를 새로고침하지 못했습니다. 잠시 후 다시 시도해 주세요.', + refreshSourcesFailedTitle: 'Skill 소스 새로고침 실패', + refreshSourcesFallback: 'Skill 소스를 새로고침하지 못했습니다. 잠시 후 다시 시도해 주세요.', + refreshBundledFailedTitle: '기본 제공 Skills 새로고침 실패', + refreshBundledFallback: '기본 제공 Skills를 새로고침하지 못했습니다. 잠시 후 다시 시도해 주세요.', + installBundledFailedTitle: '기본 제공 Skill 설치 실패', + installBundledFallback: '기본 제공 Skill을 설치하지 못했습니다. 잠시 후 다시 시도해 주세요.', + installedBundledTitle: '기본 제공 Skill 설치됨', + installedDescription: (id: string) => `${id}/SKILL.md가 현재 작업 공간에 추가되었습니다.`, + importSourceFailedTitle: 'Skill 소스 가져오기 실패', + importSourceFallback: 'Skill 소스를 가져오지 못했습니다. 잠시 후 다시 시도해 주세요.', + importedSourceTitle: 'Skill 소스 가져오기 완료', + installFailedTitle: 'Skill 설치 실패', + installFallback: 'Skill을 설치하지 못했습니다. 잠시 후 다시 시도해 주세요.', + installedTitle: 'Skill 설치됨', + previewFailedTitle: 'Skill 업데이트 미리 보기 실패', + previewFallback: 'Skill 업데이트를 미리 보지 못했습니다. 잠시 후 다시 시도해 주세요.', + updateFailedTitle: 'Skill 업데이트 실패', + updateFallback: 'Skill을 업데이트하지 못했습니다. 잠시 후 다시 시도해 주세요.', + updatedTitle: 'Skill 업데이트됨', + forceUpdatedTitle: 'Skill을 업데이트로 덮어씀', + updatedDescription: (id: string) => `${id}/SKILL.md가 소스 라이브러리 버전으로 업데이트되었습니다.`, + toggleFailedTitle: 'Skill 상태 변경 실패', + toggleFallback: 'Skill 상태를 변경하지 못했습니다. 잠시 후 다시 시도해 주세요.', + enabledTitle: 'Skill 켜짐', + disabledTitle: 'Skill 꺼짐', + pinnedTitle: 'Skill이 컨텍스트에 고정됨', + unpinnedTitle: 'Skill 고정 해제됨', + runtimeDescription: (name: string) => `현재 프로젝트의 ${name} 런타임 상태가 업데이트되었습니다.`, + deleteFailedTitle: 'Skill 삭제 실패', + deleteFallback: 'Skill을 삭제하지 못했습니다. 잠시 후 다시 시도해 주세요.', + deletedTitle: 'Skill 삭제됨', + deletedDescription: (id: string) => `${id} 항목이 제거되었습니다.`, + openFailedTitle: 'Skill 열기 실패', + openFallback: 'Skill을 열지 못했습니다. 잠시 후 다시 시도해 주세요.', + openFailures: { + invalid_id: '허용되지 않는 Skill 이름입니다.', + missing: '해당하는 SKILL.md를 찾을 수 없습니다.', + blocked_path: 'Skill 경로가 작업 공간의 skills 폴더 밖에 있어 열기가 차단되었습니다.', + not_file: '대상이 열 수 있는 SKILL.md 파일이 아닙니다.', + not_directory: '대상이 열 수 있는 폴더가 아닙니다.', + open_failed: '시스템에서 파일을 열지 못했습니다.', + }, + sourceFailures: { + invalid_skill: '올바른 SKILL.md 파일을 선택하세요.', + already_exists: '소스 라이브러리에 같은 이름의 Skill이 이미 있습니다.', + blocked_path: '이 파일 경로는 가져올 수 없습니다.', + write_failed: '소스 라이브러리에 쓰지 못했습니다. 파일 권한을 확인해 주세요.', + cancelled: '취소되었습니다.', + }, + installFailures: { + not_found: '이 Skill 소스를 찾을 수 없습니다.', + already_exists: '이 작업 공간에 같은 이름의 Skill이 이미 있습니다.', + blocked_path: '대상 경로에 쓸 수 없습니다.', + write_failed: '작업 공간에 쓰지 못했습니다. 파일 권한을 확인해 주세요.', + }, + updateFailures: { + not_managed: '이 Skill은 관리되는 소스에서 온 것이 아닙니다.', + source_missing: '소스 라이브러리에서 해당 소스를 찾을 수 없습니다.', + local_modified: + '작업 공간의 사본이 수정되었습니다. 업데이트하기 전에 로컬 파일과 소스 파일을 열어 비교하세요.', + metadata_error: 'Skill 메타데이터가 올바르지 않아 안전하게 업데이트할 수 없습니다.', + blocked_path: '대상 경로에 쓸 수 없습니다.', + write_failed: '작업 공간에 쓰지 못했습니다. 파일 권한을 확인해 주세요.', + }, + previewFailures: { + not_managed: '이 Skill은 관리되는 소스에서 온 것이 아닙니다.', + source_missing: '소스 라이브러리에서 해당 소스를 찾을 수 없습니다.', + metadata_error: 'Skill 메타데이터가 올바르지 않아 안전하게 미리 볼 수 없습니다.', + blocked_path: '대상 경로를 읽을 수 없습니다.', + read_failed: 'Skill 내용을 읽지 못했습니다. 파일 권한을 확인해 주세요.', + }, + deleteFailures: { + not_found: '현재 작업 공간에서 이 Skill을 찾을 수 없습니다.', + blocked_path: '이 Skill 경로는 삭제할 수 없습니다.', + blocked_scope: '프로젝트 Skills는 저장소에서 관리합니다. 대신 프로젝트에서 삭제해 주세요.', + delete_failed: 'Skill을 삭제하지 못했습니다. 파일 권한을 확인해 주세요.', + }, + runtimeFailures: { + not_found: '현재 작업 공간에서 이 Skill을 찾을 수 없습니다.', + blocked_path: 'Skill 상태 경로에 쓸 수 없습니다.', + state_error: '이 작업 공간의 Skill 상태 파일이 올바르지 않아 먼저 고쳐야 합니다.', + write_failed: '현재 프로젝트의 Skill 상태를 쓰지 못했습니다. 파일 권한을 확인해 주세요.', + }, + }, + sessionSettingsActions: { + bypassConfirmTitle: '전체 접근으로 전환할까요?', + bypassConfirmDescription: + "로컬 도구가 Maka의 보호 계층을 거치지 않고 파일을 직접 읽고 쓰며 네트워크에 직접 접근합니다. 완전히 신뢰하는 작업이나 실행 환경으로 이미 격리된 작업에만 사용하세요.", + bypassConfirmLabel: '전체 접근 켜기', + bypassCancelLabel: '자동 유지', + permissionFailedTitle: '권한 모드 변경 실패', + permissionFallback: '권한 모드를 변경하지 못했습니다. 잠시 후 다시 시도해 주세요.', + attachmentIngestBlocked: { + item_too_large: '첨부 파일 하나가 크기 제한을 넘습니다.', + items_invalid: '첨부 파일 목록이 올바르지 않습니다. 파일을 다시 선택해 다시 보내 주세요.', + count_limit: '메시지 하나에 첨부 파일은 최대 8개까지입니다.', + duplicate_source: '첨부 파일 원본이 중복되었습니다. 같은 파일을 두 번 추가하지 마세요.', + total_size_exceeded: '첨부 파일 전체 크기가 제한을 넘습니다.', + source_expired: '첨부 파일 원본이 만료되었거나 올바르지 않습니다. 파일을 다시 선택해 다시 보내 주세요.', + }, + modelFailedTitle: '모델 변경 실패', + modelFallback: '모델을 변경하지 못했습니다. 잠시 후 다시 시도해 주세요.', + thinkingFailedTitle: '사고 수준 변경 실패', + thinkingFallback: '사고 수준을 변경하지 못했습니다. 잠시 후 다시 시도해 주세요.', + }, + goalDialog: { + title: '목표 설정', + description: 'Maka는 목표를 달성하거나, 불가능하다고 판단하거나, 아래 예산에 도달할 때까지 턴마다 스스로 이어서 진행합니다. 입력창 위에서 언제든지 멈출 수 있습니다.', + conditionLabel: '완료 조건', + conditionDescription: '무엇을 완료로 볼지 한 문장으로 적으세요. Maka가 매 턴이 끝날 때마다 확인합니다.', + conditionPlaceholder: '예: 모든 테스트가 통과하고 lint 경고가 없음', + maxIterationsLabel: '최대 턴 수', + maxIterationsDescription: '비워 두면 기본값을 사용합니다.', + maxIterationsInvalid: (max) => `1부터 ${max}까지의 정수를 입력하거나 비워 두세요.`, + tokenBudgetLabel: '토큰 예산', + tokenBudgetDescription: '비워 두면 토큰 상한이 없습니다.', + tokenBudgetInvalid: (min) => `${min} 이상의 정수를 입력하거나 비워 두세요.`, + cancel: '취소', + close: '닫기', + submit: '시작', + failedFallback: '목표를 설정하지 못했습니다. 다시 시도해 주세요.', + statusLabels: { + active: '진행 중', + waiting: '대기 중', + paused: '일시 중지됨', + achieved: '달성됨', + impossible: '불가능', + cleared: '지워짐', + stalled: '정체됨', + budget_limited: '토큰 예산 도달', + max_iterations: '최대 턴 수 도달', + }, + reconciledMatching: (condition, status) => + `다시 연결한 뒤의 현재 목표: “${condition}” (${status}). 요청과 일치하지만, 중단된 요청이 실제로 반영되었다는 뜻은 아닙니다.`, + reconciledDifferent: (condition, status) => + `다시 연결한 뒤의 현재 목표: “${condition}” (${status}). 이 요청과 다릅니다.`, + reconciledNoGoal: '다시 연결한 뒤의 현재 상태: 목표를 찾을 수 없습니다.', + reconciliationUnavailable: '연결이 끊겨 현재 목표를 아직 확인할 수 없습니다. 닫았다가 다시 열어 확인해 주세요. 이 대화상자는 두 번 제출되지 않습니다.', + }, + errorBoundary: { + copyPending: '복사하는 중…', + copied: '복사됨', + copyFailed: '복사 실패', + copyReport: '진단 정보 복사', + title: 'Maka 렌더러가 비정상 종료되었습니다', + description: + '처리되지 않은 React 오류를 포착했습니다. 다시 시도해 이 오류 화면을 닫거나, 다시 불러와 창 전체를 새로고침해 주세요. 이슈를 전달하기 전에 진단 정보를 복사하세요.', + retry: '다시 시도', + reload: '다시 불러오기', + clipboardFailure: '클립보드를 사용할 수 없거나 접근이 거부되었습니다. 잠시 후 다시 시도해 주세요.', + }, + commandPalette: { + label: '명령 팔레트', + searchLabel: '명령 팔레트 검색', + placeholder: '명령, 설정, 작업 검색…', + closeLabel: '명령 팔레트 닫기', + resultsLabel: '명령 팔레트 결과', + emptyTitle: '일치하는 명령 없음', + emptyDescription: '다른 검색어를 입력하거나 Esc를 눌러 닫으세요.', + selectHint: '선택', + runHint: '실행', + closeHint: '닫기', + current: '현재', + groups: { + settings: '설정', + permissions: '권한', + connections: '연결', + conversations: '작업', + }, + staticKeywords: STATIC_COMMAND_KEYWORDS, + commands: KO_STATIC_COMMANDS, + settingsSections: KO_SETTINGS_SECTIONS, + permissionModes: { + explore: { + label: '권한 · 읽기 전용', + hint: '직접 읽고 검색합니다. 쓰기와 네트워크 접근은 확인을 받습니다', + }, + ask: { + label: '권한 · 자동', + hint: "Maka의 보호 계층 안에서 실행합니다. 현재 권한을 넘어서는 동작은 먼저 묻습니다", + }, + bypass: { + label: '권한 · 전체 접근', + hint: "Maka의 보호 계층을 거치지 않고 파일과 네트워크에 직접 접근합니다", + }, + }, + settingsCommand: (section: string) => `설정 · ${section}`, + testDefaultConnection: (name: string) => `기본 연결 테스트 · ${name}`, + setDefaultConnection: (name: string) => `기본값으로 설정 · ${name}`, + testConnection: (name: string) => `연결 테스트 · ${name}`, + settingsKeywords: (section: SettingsSection, label: string) => [section, label, 'settings', '설정'], + permissionKeywords: (mode: PermissionMode) => [mode, 'permission', 'mode', '권한', '모드'], + connectionKeywords: (action: 'default' | 'test', name: string, providerType: string) => [ + action, + 'connection', + '연결', + '기본', + '테스트', + name, + providerType, + ], + }, + keyboardHelp: { + title: '키보드 단축키', + sections: [ + { + heading: '일반', + rows: [ + { + keys: ['⌘', 'K'], + description: '명령 팔레트 열기(작업, 설정, 테마 등)', + }, + { keys: ['?'], description: '이 단축키 패널 열기 또는 닫기' }, + { keys: ['⌘', 'N'], description: '새 작업 만들기' }, + { keys: ['⌘', ','], description: '설정 열기' }, + { + keys: ['⌘', 'Shift', 'D'], + description: '현재 컨텍스트의 진단 정보 복사', + }, + { keys: ['Esc'], description: '현재 대화상자 닫기' }, + ], + }, + { + heading: '입력창', + rows: [ + { keys: ['Enter'], description: '메시지 보내기' }, + { keys: ['Shift', 'Enter'], description: '줄 바꿈 넣기' }, + { + keys: ['Alt', 'Enter'], + description: '줄 바꿈 넣기(대체 키)', + }, + ], + }, + { + heading: '작업 목록', + rows: [ + { + keys: ['Tab'], + description: '작업과 내비게이션 사이에서 포커스 이동', + }, + { + keys: ['↑', '↓'], + description: '포커스된 작업 사이 이동', + }, + { + keys: ['Home', 'End'], + description: '목록의 맨 위 또는 맨 아래로 이동', + }, + { keys: ['Enter'], description: '포커스된 작업 열기' }, + { + keys: ['Delete'], + description: '삭제 확인 창 열기(확인 없이 삭제하지 않음)', + }, + { + keys: ['F'], + description: '작업 검색으로 포커스 이동(Esc를 누르면 지워짐)', + }, + ], + }, + { + heading: '채팅', + rows: [ + { + keys: ['Tab'], + description: '도구 활동과 복사 버튼으로 포커스 이동', + }, + { + keys: ['Space', 'Enter'], + description: '도구 호출 펼치기 또는 접기', + }, + ], + }, + { + heading: '패널 크기 조절', + rows: [ + { keys: ['Tab'], description: '왼쪽 또는 오른쪽 분할선으로 포커스 이동' }, + { + keys: ['←', '→'], + description: '작업 목록 너비 조절(±10px)', + }, + { + keys: ['Shift', '←', '→'], + description: '빠르게 조절(±50px)', + }, + { + keys: ['Home', 'End'], + description: '최소 또는 최대 너비로 바로 이동', + }, + ], + }, + ], + }, + chrome: { + windowActions: '창 바로 가기', + searchConversations: '작업 검색', + expandSidebar: '사이드바 펼치기', + collapseSidebar: '사이드바 접기', + newTask: '새 작업', + expandWorkbar: '작업 워크바 펼치기', + collapseWorkbar: '작업 워크바 접기', + workspaceActions: '작업 공간 동작', + }, + app: { + returnToWorkHub: 'WorkHub로 돌아가기', + loadingWorkbarLabel: '작업 워크바 불러오는 중', + loadingWorkbar: '작업 워크바 불러오는 중…', + useSkillPrompt: (skillName: string) => `${skillName} Skill 사용: `, + newConversation: '새 작업', + compactSuccessTitle: '컨텍스트 압축됨', + compactSuccessDescription: '이전 컨텍스트를 체크포인트 요약으로 대체했습니다.', + compactStartedTitle: '컨텍스트 압축하는 중', + compactStartedDescription: '이전 컨텍스트를 체크포인트로 요약하는 중입니다.', + compactUnchangedTitle: '압축할 내용 없음', + compactUnchangedDescription: '작업이 이미 최신 체크포인트를 사용하고 있습니다.', + compactErrorTitle: '압축 실패', + compactErrorFallback: '작업을 압축하지 못했습니다. 잠시 후 다시 시도해 주세요.', + slashCommands: { + compact: { name: '컨텍스트 압축', description: '현재 작업은 유지하면서 이전 기록을 압축합니다' }, + graph: { name: 'Graph 사용', description: 'Graph를 확인하거나 전환하거나 한 번 실행합니다' }, + side: { name: '사이드 채팅 열기', description: '사이드 패널에서 특정 주제를 시작합니다' }, + swarm: { name: 'Swarm 사용', description: 'Swarm을 확인하거나 전환하거나 한 번 실행합니다' }, + }, + sideChatUnavailableTitle: '사이드 채팅을 아직 사용할 수 없음', + sideChatUnavailableDescription: + '/side를 사용하기 전에 메인 작업에서 메시지를 보내세요.', + sideChatContextPendingTitle: '대기 중인 컨텍스트를 먼저 처리하세요', + sideChatContextPendingDescription: + '입력창에 아직 첨부 파일, 인용, 파일 멘션이 있습니다. /side를 사용하기 전에 보내거나 제거하세요.', + resumeStartedTitle: '이 턴을 이어서 진행하는 중', + resumeStartedDescription: '마지막으로 완료된 실행 경계부터 이어서 진행합니다', + resumeFailedTitle: '이어서 진행 실패', + resumeFailedFallback: '이 턴을 이어서 진행하지 못했습니다. 작업 상태를 확인한 뒤 다시 시도해 주세요.', + goalClearFailedTitle: '목표 중지 실패', + goalClearFailedFallback: '목표가 아직 실행 중일 수 있습니다. 지금 다시 시도해 주세요.', + goalPauseFailedTitle: '목표 일시 중지 실패', + goalPauseFailedFallback: '목표가 아직 계속 진행 중일 수 있습니다. 지금 다시 시도해 주세요.', + goalResumeFailedTitle: '목표 재개 실패', + goalResumeFailedFallback: '목표가 아직 일시 중지된 상태입니다. 다시 시도해 주세요.', + appearanceLoadErrorTitle: '모양 설정 불러오기 실패', + appearanceLoadErrorFallback: '모양 설정을 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요.', + memoryRefreshErrorTitle: '로컬 메모리 상태 새로고침 실패', + memoryLoadErrorTitle: '로컬 메모리 상태 불러오기 실패', + memoryErrorFallback: '로컬 메모리 상태를 새로고침하지 못했습니다. 잠시 후 다시 시도해 주세요.', + openModelSettings: '설정 열기 · 모델', + configureModelsOnHost: (hostName: string) => + `작업을 시작하기 전에 ${hostName}에서 모델 연결을 설정하세요.`, + sidebarCollapsed: '사이드바가 접혀 있음', + resizeConversationList: '작업 목록 크기 조절', + skipErrorTitle: '온보딩 건너뛰기 실패', + tryAgainLater: '잠시 후 다시 시도해 주세요.', + loading: '불러오는 중', + goToModels: '모델로 이동', + boundaryUnreadableTitle: '이 작업의 권한 읽기 실패', + boundaryUnreadableDetail: + '권한을 읽을 수 있을 때까지 여기에 입력할 수 없습니다. 다시 시도하거나 다른 작업으로 전환해 주세요.', + boundaryUnreadableRetry: '다시 시도', + boundaryUnreadableRetrying: '다시 시도하는 중…', + permissionModeStreaming: + '이 작업의 응답을 스트리밍하는 중입니다. 완료될 때까지 기다린 뒤 권한 모드를 변경하세요.', + permissionModeRunning: '이 작업이 실행 중입니다. 완료될 때까지 기다린 뒤 권한 모드를 변경하세요.', + permissionModeWaiting: '도구 호출이 확인을 기다리고 있습니다. 먼저 응답한 뒤 권한 모드를 변경하세요.', + modeChangeLoading: '이 작업을 아직 불러오는 중입니다. 잠시 후 모드를 변경할 수 있습니다.', + modeChanging: '모드를 변경하는 중입니다. 완료될 때까지 기다린 뒤 계속하세요.', + modeChangeStreaming: '이 작업의 응답을 스트리밍하는 중입니다. 완료될 때까지 기다린 뒤 모드를 변경하세요.', + modeChangeRunning: '이 작업이 실행 중입니다. 완료될 때까지 기다린 뒤 모드를 변경하세요.', + modeChangeWaiting: '도구 호출이 확인을 기다리고 있습니다. 먼저 응답한 뒤 모드를 변경하세요.', + goalTurnActive: + '목표는 다음 턴부터 적용됩니다. 이 턴이 끝날 때까지 기다린 뒤 설정하세요.', + planModeFailedTitle: 'Plan Mode 변경 실패', + planModeFallback: 'Plan Mode를 변경하지 못했습니다. 잠시 후 다시 시도해 주세요.', + orchestrationModeFailedTitle: '오케스트레이션 모드 변경 실패', + orchestrationModeFallback: '오케스트레이션 모드를 변경하지 못했습니다. 잠시 후 다시 시도해 주세요.', + planModeExitPendingTitle: '현재 계획을 포기할까요?', + planModeExitPendingDescription: (title: string) => + `“${title}” 계획이 아직 승인되지 않았습니다. Plan Mode를 나가면 기록은 보존되고 이 계획은 포기됨으로 표시됩니다.`, + planModeExitConfirm: '포기하고 나가기', + planModeExitCancel: '계속 계획하기', + planModeExecutionActiveTitle: '계획이 아직 실행 중입니다', + planModeExecutionActiveDescription: '계획을 수정하려면 진행 중인 실행을 중단한 뒤 Plan Mode에 들어가세요.', + swarmModeEnabledTitle: 'Swarm Mode 켜짐', + swarmModeDisabledTitle: 'Swarm Mode 꺼짐', + swarmModeStatusDescription: '/swarm on, /swarm off를 사용하거나, 한 턴에만 쓰려면 /swarm <작업>을 사용하세요.', + graphModeEnabledTitle: 'Graph Mode 켜짐', + graphModeDisabledTitle: 'Graph Mode 꺼짐', + graphModeStatusDescription: '/graph on, /graph off를 사용하거나, 한 턴에만 쓰려면 /graph <작업>을 사용하세요.', + graphHistoryTitle: 'Graph 기록', + graphHistoryDescription: 'Agent Graph 패널의 실행 메뉴에서 기록을 확인하세요.', + resizeWorkbar: '작업 워크바 크기 조절', + }, + }, } satisfies UiCatalog; export function getShellCopy(locale: UiLocale): ShellCopy { From 0dfd52eeded42617b60aacf04cc1a456f043588c Mon Sep 17 00:00:00 2001 From: heeeione Date: Thu, 17 Sep 2026 18:38:40 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat(i18n-ko):=20desktop=20=E2=80=94=20ask?= =?UTF-8?q?=20rather=20than=20tell=20in=20the=20retired-connection=20onboa?= =?UTF-8?q?rding=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 #3977 Generated-by: Claude Code --- apps/desktop/src/renderer/locales/onboarding-copy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/renderer/locales/onboarding-copy.ts b/apps/desktop/src/renderer/locales/onboarding-copy.ts index 99df2a1e2c..2fd5f52d47 100644 --- a/apps/desktop/src/renderer/locales/onboarding-copy.ts +++ b/apps/desktop/src/renderer/locales/onboarding-copy.ts @@ -228,7 +228,7 @@ const ONBOARDING_COPY_BY_LOCALE: UiCatalog = { 'blocked:all_connections_retired': { eyebrow: '연결 확인 필요', title: '연결에서 사용하는 로그인 방식의 지원이 종료되었습니다.', - body: '이 연결들이 사용하는 로그인 방식이 Maka에서 제거되었습니다. 더 이상 로그인하거나 대화에 사용할 수 없습니다. 계속하려면 새 모델 연결을 추가하세요.', + body: '이 연결들이 사용하는 로그인 방식이 Maka에서 제거되었습니다. 더 이상 로그인하거나 대화에 사용할 수 없습니다. 계속하려면 새 모델 연결을 추가해 주세요.', cta: { label: '모델 연결 추가' }, tone: 'destructive', }, From 2d0002686d5688e6eaab750e68618681545f4e12 Mon Sep 17 00:00:00 2001 From: Heewon Chae <68272931+heeoneie@users.noreply.github.com> Date: Fri, 18 Sep 2026 13:48:29 +0900 Subject: [PATCH 7/8] =?UTF-8?q?test(i18n-ko):=20desktop=20=E2=80=94=20expe?= =?UTF-8?q?ct=20the=20ko=20row=20in=20the=20language=20selector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding the `ko` row to `localeOptions` left this test asserting the old four-entry arrays, so the three `deepEqual` assertions for `zh-CN`, `zh-TW` and `en` failed on this branch. They now carry the `ko` row in the position `UI_LOCALES` gives it, between `zh-TW` and `en`: the endonym `한국어` in the `zh-CN`, `zh-TW` and `ko` catalogs, and `Korean` in `en`. The Korean catalog gets an assertion of its own, since a selector the user cannot reach in Korean would satisfy the other three. Verified by running the file with `tsx --test`: 1 of 5 tests failed at `0dfd52eed` and all 5 pass here. Every expected string was compared against the catalog by script rather than typed by hand. `tsc -p tsconfig.main.json` holds at 555 errors, unchanged from `0dfd52eed`, and `biome check` is clean on the file. Refs #3977 Generated-by: Claude Code --- .../main/__tests__/settings-preferences-copy.test.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/desktop/src/main/__tests__/settings-preferences-copy.test.ts b/apps/desktop/src/main/__tests__/settings-preferences-copy.test.ts index c0c5a05203..62011cdfed 100644 --- a/apps/desktop/src/main/__tests__/settings-preferences-copy.test.ts +++ b/apps/desktop/src/main/__tests__/settings-preferences-copy.test.ts @@ -29,18 +29,28 @@ test('language selector offers every preference with locale-appropriate labels', ['auto', '跟随系统'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], + ['ko', '한국어'], ['en', 'English'], ]); assert.deepEqual(getSettingsPreferencesCopy('zh-TW').personalization.localeOptions, [ ['auto', '自動(跟隨系統)'], ['zh-CN', '简体中文'], ['zh-TW', '繁體中文'], + ['ko', '한국어'], + ['en', 'English'], + ]); + assert.deepEqual(getSettingsPreferencesCopy('ko').personalization.localeOptions, [ + ['auto', '시스템 설정 따름'], + ['zh-CN', '简体中文'], + ['zh-TW', '繁體中文'], + ['ko', '한국어'], ['en', 'English'], ]); assert.deepEqual(getSettingsPreferencesCopy('en').personalization.localeOptions, [ ['auto', 'Follow system'], ['zh-CN', 'Simplified Chinese'], ['zh-TW', 'Traditional Chinese'], + ['ko', 'Korean'], ['en', 'English'], ]); }); From f61172ff27379bf210f968625cf0c51c923ab394 Mon Sep 17 00:00:00 2001 From: Heewon Chae <68272931+heeoneie@users.noreply.github.com> Date: Fri, 18 Sep 2026 14:03:45 +0900 Subject: [PATCH 8/8] =?UTF-8?q?feat(i18n-ko):=20desktop=20=E2=80=94=20add?= =?UTF-8?q?=20Korean=20search=20keywords=20for=20the=20command=20palette?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `STATIC_COMMAND_KEYWORDS` is one shared map rather than a per-locale one, so each command carries the English and Simplified Chinese words its Korean label does not reach. This adds the Korean equivalents, 36 words across 23 of the 25 commands. The palette matches a query as a subsequence of the label, the hint and then the keywords, so Korean already searched: `새작업` finds `새 작업` because the match skips the space. Unspaced variants are therefore not listed. What the label cannot reach is a different word for the same thing, which is what the existing `chats`, `追问` and `帮助` entries are for, so each addition mirrors a concept those entries already cover: `대화`/`채팅` for `chat`, `도움말` for `help`, `어두운`/`야간` for `深色`/`night`. Search keywords take the words a user is likely to type, so they include transliterations the glossary rejected for the copy itself: `워크스페이스` for Workspace, `스킬` for Skills, `메모리` for MEMORY.md. The copy stays normative; the search is permissive. `action:new-deep-research` and `diag:open-project-folder` get nothing. Their labels and hints already carry every concept their English and Chinese keywords cover. Three commands needed `데일리 리뷰` because their labels say `오늘의 리뷰`, so the feature's own name did not find them. Verified by script: each of the 36 words resolves to its command through the label, hint or keyword list, and none duplicates a word already reachable there. `tsc -p tsconfig.renderer.json` holds at 602 errors, unchanged, and `biome check` is clean. No test covers this map. Refs #3977 Generated-by: Claude Code --- .../src/renderer/locales/shell-copy.ts | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/desktop/src/renderer/locales/shell-copy.ts b/apps/desktop/src/renderer/locales/shell-copy.ts index a376f7a022..68b5541d9a 100644 --- a/apps/desktop/src/renderer/locales/shell-copy.ts +++ b/apps/desktop/src/renderer/locales/shell-copy.ts @@ -74,7 +74,7 @@ type CommandCopy = { }; const STATIC_COMMAND_KEYWORDS: Record = { - 'action:new-chat': ['new', 'chat', 'start', '新', '建', '任务'], + 'action:new-chat': ['new', 'chat', 'start', '新', '建', '任务', '대화', '채팅'], 'action:side-chat': [ 'side', 'chat', @@ -84,24 +84,24 @@ const STATIC_COMMAND_KEYWORDS: Record = { '侧边', '侧聊', '任务', - '追问', + '追问', '질문', ], 'action:new-deep-research': ['deep', 'research', 'explore', 'readonly', '研究', '深度', '探索', '只读'], - 'action:new-scheduled-task': ['plan', 'task', 'schedule', 'new', 'create', '计划', '提醒', '新建', '创建'], - 'action:open-settings': ['settings', 'preferences', '设置', 'options'], - 'action:keyboard-help': ['shortcuts', 'keyboard', 'help', '快捷键', '帮助'], - 'theme:light': ['light', 'theme', '浅色', '主题'], - 'theme:dark': ['dark', 'theme', '深色', 'night', '主题'], - 'theme:auto': ['auto', 'system', 'theme', '跟随', '系统', '主题'], - 'nav:sessions': ['sessions', 'chats', '任务', '会话', '对话', 'left'], - 'nav:automations': ['automations', 'plan', 'task', 'schedule', 'cron', '定时任务', '计划', '提醒'], - 'nav:skills': ['skills', '技能'], - 'nav:mcp': ['mcp', 'server', 'tools', '扩展', '工具'], - 'nav:daily-review': ['daily', 'review', 'today', '每日', '回顾', '今天'], - 'diag:open-workspace': ['workspace', 'folder', 'open', 'finder', '工作区', '文件夹', '目录'], + 'action:new-scheduled-task': ['plan', 'task', 'schedule', 'new', 'create', '计划', '提醒', '新建', '创建', '예약', '일정'], + 'action:open-settings': ['settings', 'preferences', '设置', 'options', '환경설정'], + 'action:keyboard-help': ['shortcuts', 'keyboard', 'help', '快捷键', '帮助', '도움말'], + 'theme:light': ['light', 'theme', '浅色', '主题', '밝은'], + 'theme:dark': ['dark', 'theme', '深色', 'night', '主题', '어두운', '야간'], + 'theme:auto': ['auto', 'system', 'theme', '跟随', '系统', '主题', '자동'], + 'nav:sessions': ['sessions', 'chats', '任务', '会话', '对话', 'left', '세션', '대화'], + 'nav:automations': ['automations', 'plan', 'task', 'schedule', 'cron', '定时任务', '计划', '提醒', '정기 작업', '예약'], + 'nav:skills': ['skills', '技能', '스킬'], + 'nav:mcp': ['mcp', 'server', 'tools', '扩展', '工具', '서버', '도구'], + 'nav:daily-review': ['daily', 'review', 'today', '每日', '回顾', '今天', '오늘'], + 'diag:open-workspace': ['workspace', 'folder', 'open', 'finder', '工作区', '文件夹', '目录', '워크스페이스'], 'diag:open-project-folder': ['project', 'folder', 'open', 'finder', '项目', '目录', '文件夹'], - 'diag:open-skills': ['skills', 'folder', 'open', 'finder', '技能', '文件夹'], - 'diag:export-conversation': ['export', 'markdown', 'copy', 'conversation', '导出', '任务', '剪贴板', 'md'], + 'diag:open-skills': ['skills', 'folder', 'open', 'finder', '技能', '文件夹', '스킬'], + 'diag:export-conversation': ['export', 'markdown', 'copy', 'conversation', '导出', '任务', '剪贴板', 'md', '내보내기', '마크다운'], 'diag:save-conversation-file': [ 'save', 'file', @@ -112,10 +112,10 @@ const STATIC_COMMAND_KEYWORDS: Record = { '文件', '任务', '导出', - 'md', + 'md', '내보내기', '마크다운', ], - 'diag:copy-today-daily-review': ['daily', 'review', 'today', 'copy', 'markdown', '今日', '回顾', '复制', '剪贴板'], - 'diag:paste-today-daily-review': ['daily', 'review', 'paste', 'composer', '今日', '回顾', '粘贴', '输入框'], + 'diag:copy-today-daily-review': ['daily', 'review', 'today', 'copy', 'markdown', '今日', '回顾', '复制', '剪贴板', '데일리 리뷰'], + 'diag:paste-today-daily-review': ['daily', 'review', 'paste', 'composer', '今日', '回顾', '粘贴', '输入框', '데일리 리뷰'], 'diag:save-today-daily-review': [ 'daily', 'review', @@ -127,7 +127,7 @@ const STATIC_COMMAND_KEYWORDS: Record = { '回顾', '保存', '文件', - '导出', + '导出', '데일리 리뷰', '내보내기', ], 'diag:copy-diagnostics': [ 'env', @@ -143,10 +143,10 @@ const STATIC_COMMAND_KEYWORDS: Record = { '日志', '关于', '诊断', - '汇报', + '汇报', '환경', '버전', '로그', '버그', '보고', ], - 'diag:test-network-proxy': ['network', 'proxy', 'test', 'ping', '网络', '代理', '测试', '连接', '诊断'], - 'diag:open-local-memory': ['memory', 'md', 'open', '记忆', '本地', '编辑', 'edit'], + 'diag:test-network-proxy': ['network', 'proxy', 'test', 'ping', '网络', '代理', '测试', '连接', '诊断', '연결'], + 'diag:open-local-memory': ['memory', 'md', 'open', '记忆', '本地', '编辑', 'edit', '메모리'], }; type ShellCopy = {