Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/desktop/e2e/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ async function seedE2eConnection(userDataDir: string): Promise<void> {
}
}

async function seedE2eLocale(userDataDir: string, locale: 'zh-CN' | 'zh-TW' | 'en'): Promise<void> {
async function seedE2eLocale(userDataDir: string, locale: 'zh-CN' | 'zh-TW' | 'en' | 'ko'): Promise<void> {
const workspaceRoot = path.join(userDataDir, 'workspaces', 'default');
await createSettingsStore(workspaceRoot).update({
personalization: { uiLocale: locale },
Expand Down Expand Up @@ -408,7 +408,7 @@ export async function withE2eWindow(
seed: boolean;
readinessSelector: string;
e2eFixtureScenario?: string;
locale?: 'zh-CN' | 'zh-TW' | 'en';
locale?: 'zh-CN' | 'zh-TW' | 'en' | 'ko';
/** #1312: force app:info's platform so the window boots natively into that platform's `data-os` cascade. */
platform?: 'darwin' | 'win32' | 'linux';
/** Show fixtures whose contract depends on compositor-paced frames. */
Expand Down
46 changes: 46 additions & 0 deletions apps/desktop/e2e/ko-locale.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { ensureSidebarExpanded, expect, test } from './fixtures';

test('persists Korean locale preference through reload', async ({ window: page }) => {
await ensureSidebarExpanded(page);
await page.getByRole('button', { name: 'Settings', exact: true }).click();
await expect(page.getByRole('main', { name: 'Settings content' })).toBeVisible();
await page.getByRole('button', { name: 'General', exact: true }).click();
await expect(page.getByText('UI language', { exact: true }).first()).toBeVisible();
await page.keyboard.press('Escape');

await page.evaluate(async () => {
await window.maka.settings.update({ personalization: { uiLocale: 'ko' } });
});
await page.reload();
await page.waitForSelector('.maka-composer-editor');
await ensureSidebarExpanded(page);

const locale = await page.evaluate(async () => {
const settings = await window.maka.settings.read();
return settings.personalization.uiLocale;
});
expect(locale).toBe('ko');

await page.getByRole('button', { name: 'Settings', exact: true }).click();
await page.getByRole('button', { name: 'General', exact: true }).click();
await expect(page.getByText('UI language', { exact: true }).first()).toBeVisible();
});
12 changes: 10 additions & 2 deletions apps/desktop/src/main/__tests__/e2e-fixture-locale.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import assert from 'node:assert/strict';
import { test } from 'node:test';
import { resolveE2eFixture } from '../e2e-fixture.js';

test('preserves both canonical Chinese locale fixture flags', () => {
for (const locale of ['zh-CN', 'zh-TW', 'en'] as const) {
test('preserves canonical Chinese and Korean locale fixture flags', () => {
for (const locale of ['zh-CN', 'zh-TW', 'en', 'ko'] as const) {
const fixture = resolveE2eFixture(
'settings-general',
false,
Expand All @@ -39,6 +39,14 @@ test('normalizes locale fixture flag casing without widening the contract', () =
resolveE2eFixture('settings-general', false, undefined, undefined, 'ZH-tw')?.locale,
'zh-TW',
);
assert.equal(
resolveE2eFixture('settings-general', false, undefined, undefined, 'KO')?.locale,
'ko',
);
assert.equal(
resolveE2eFixture('settings-general', false, undefined, undefined, 'ko-kr')?.locale,
'ko',
);
assert.equal(
resolveE2eFixture('settings-general', false, undefined, undefined, 'zh-Hant')?.locale,
null,
Expand Down
7 changes: 7 additions & 0 deletions apps/desktop/src/main/client-settings-confirmation-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ const COPY = {
message: "Allow Maka to update this client's settings?",
buttons: ['Apply changes', 'Cancel'],
},
ko: {
labels: { theme: 'Theme', palette: 'Palette', uiLocale: 'UI language', runComplete: 'Run-complete notifications', keepSystemAwake: 'Keep system awake' },
on: 'true',
off: 'false',
message: "Allow Maka to update this client's settings?",
buttons: ['Apply changes', 'Cancel'],
},
} satisfies UiCatalog<ConfirmationCopy>;

export function clientSettingsConfirmation(
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/main/computer-use/status-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ const COPY: UiCatalog<StatusItemCopy> = {
stopUnnamed: 'Stop Computer Use',
empty: 'No Active Sessions',
},
ko: {
stopUsing: (appName) => `${appName} 사용 중지`,
stopUnnamed: 'Computer Use 중지',
empty: '활성 세션 없음',
},
};

function defaultResolveLocale(): UiLocale {
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/main/e2e-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ function parseLocaleFlag(raw: string | undefined): UiLocale | null {
const normalized = raw?.trim().toLowerCase();
if (normalized === 'zh-cn') return 'zh-CN';
if (normalized === 'zh-tw') return 'zh-TW';
if (normalized === 'ko' || normalized === 'ko-kr') return 'ko';
return normalized === 'en' ? 'en' : null;
}

Expand Down
39 changes: 39 additions & 0 deletions apps/desktop/src/main/native-diagnostic-dialog-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,45 @@ const COPY = {
exit: '退出',
},
},
ko: {
dialog: {
copy: '진단 정보 복사',
copyAgain: '다시 복사',
copied: '진단 정보가 복사되었습니다. 이슈 보고서에 붙여넣을 수 있습니다.',
copyFailed: '진단 정보를 복사할 수 없습니다.',
},
fatalStartup: {
title: 'Maka 시작 실패',
message: 'Maka가 시작을 완료하지 못했습니다.',
detail: '예기치 않은 시작 오류가 발생했습니다. 진단 정보를 복사해 자세히 확인하세요.',
exit: '종료',
},
rendererGone: {
title: 'Maka 복구 필요',
message: 'Maka 인터페이스가 예기치 않게 중지되었습니다.',
detail:
'Maka를 다시 시작하지 않고 인터페이스만 복구합니다. Runtime Host, 실행 중인 작업, 백그라운드 서비스는 유지됩니다.',
recover: '인터페이스 복구',
exit: '종료',
},
defaultRuntimeHostRecovery: {
title: '기본 Runtime Host를 사용할 수 없습니다',
connectFailed: (profileName) => `${profileName}에 연결할 수 없습니다`,
detail:
'다시 시도하거나, Local을 기본 Host로 사용하거나, 현재 선택을 유지한 뒤 나중에 설정에서 해결하세요. 진단 정보를 복사하면 연결 실패를 확인할 수 있습니다.',
retry: '다시 시도',
useLocal: 'Local 사용',
keepOffline: '오프라인 유지',
},
storageRootRepair: {
title: 'Maka 작업 공간 복구 필요',
message: 'Maka가 이 작업 공간을 확인할 수 없습니다.',
detail: (workspaceRoot) =>
`디스크 ID가 변경되었을 수 있습니다. 복사된 작업 공간이 아니라 이 컴퓨터의 원래 Maka 작업 공간인 경우에만 복구하세요.\n\n${workspaceRoot}`,
repair: '작업 공간 복구',
exit: '종료',
},
},
} satisfies UiCatalog<NativeDiagnosticDialogCopy>;

export function getNativeDiagnosticDialogCopy(locale: UiLocale): NativeDiagnosticDialogCopy {
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/main/notifications-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ const RUN_NOTIFICATION_COPY = {
errored: { title: 'Conversation error', body: 'This response did not finish. Click to view details.' },
completed: { title: 'Response ready', body: 'Maka finished this response. Click to view it.' },
},
ko: {
errored: { title: 'Conversation error', body: 'This response did not finish. Click to view details.' },
completed: { title: 'Response ready', body: 'Maka finished this response. Click to view it.' },
},
} satisfies UiCatalog<Record<RunNotificationKind, RunNotificationCopy>>;

export function runNotificationCopy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,25 @@ const COPY: Catalog = {
noBundle: 'Not running from a .app bundle, so there is nothing to drag. Add it manually in System Settings.',
},
},
ko: {
accessibility: {
headline: (appName) => `위 목록에 ${appName}을(를) 끌어다 놓으면 손쉬운 사용 권한이 허용됩니다`,
fallback: '시스템 설정에서 +를 누르고 응용 프로그램에서 이 앱을 선택할 수도 있습니다.',
granted: '손쉬운 사용 권한이 허용되었습니다',
dismiss: '닫기',
dragHint: '끌기',
noBundle: '.app 번들로 실행 중이 아니어서 끌 수 없습니다. 시스템 설정에서 수동으로 추가하세요.',
},
screen_recording: {
headline: (appName) => `위 목록에 ${appName}을(를) 끌어다 놓으면 화면 기록 권한이 허용됩니다`,
fallback: '시스템 설정에서 +를 누르고 응용 프로그램에서 이 앱을 선택할 수도 있습니다.',
granted: '화면 기록 권한이 허용되었습니다',
dismiss: '닫기',
dragHint: '끌기',
restartHint: '여전히 거부된 것으로 표시되면 앱을 다시 시작하세요. macOS가 이전 거부 결과를 캐시합니다.',
noBundle: '.app 번들로 실행 중이 아니어서 끌 수 없습니다. 시스템 설정에서 수동으로 추가하세요.',
},
},
};

export function getPermissionOverlayCopy(
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/project-picker-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import type { UiCatalog, UiLocale } from '@maka/core/ui-locale';

const TITLE = { 'zh-CN': '添加项目', 'zh-TW': '新增專案', en: 'Add project' } satisfies UiCatalog<string>;
const TITLE = { 'zh-CN': '添加项目', 'zh-TW': '新增專案', en: 'Add project', ko: '프로젝트 추가' } satisfies UiCatalog<string>;

export function projectPickerTitle(locale: UiLocale): string {
return TITLE[locale];
Expand Down
16 changes: 16 additions & 0 deletions apps/desktop/src/main/runtime-host-boot-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ const STARTUP_RECOVERY_COPY = {
buttons: ['Retry', 'Use Local', 'Keep Offline'],
},
},
ko: {
storageRoot: {
title: 'Maka workspace needs repair',
message: 'Maka cannot verify this workspace.',
detail: (workspaceRoot) =>
`The disk identity may have changed. Repair only if this is the original Maka workspace on this computer, not a copied workspace.\n\n${workspaceRoot}`,
buttons: ['Repair Workspace', 'Exit'],
},
runtimeHost: {
title: 'Default Runtime Host is unavailable',
message: (profileName) => `Could not connect to ${profileName}`,
detail: (message) =>
`${message}\n\nRetry, use Local as the default Host, or keep the current selection and resolve it later in Settings.`,
buttons: ['Retry', 'Use Local', 'Keep Offline'],
},
},
} satisfies UiCatalog<StartupRecoveryCopy>;

export function getStartupRecoveryCopy(locale: UiLocale): StartupRecoveryCopy {
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop/src/main/runtime-host-quit-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,14 @@ const COPY = {
stopAndQuit: '停止工作並結束',
keepRunning: '繼續執行 Maka',
},
ko: {
title: 'Maka를 안전하게 종료할 수 없습니다',
message: '로컬 Runtime Host를 안전하게 중지하지 못했습니다. Maka가 아직 실행 중입니다.',
detail: '종료가 취소되었습니다. 다시 시도하거나 문제가 계속되면 진단 정보를 확인하세요.',
process: (pid: number) => `Runtime Host 프로세스 PID: ${pid}`,
manual:
'다시 시도해도 실패하면, 보존할 실행이 없는지 확인한 뒤 운영체제의 프로세스 관리 도구로 해당 PID를 중지하세요.',
cause: '원인',
button: '확인',
},
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ const COPY_BY_LOCALE = {
retryFailedTitle: 'Could not retry update download',
retryFailedFallback: 'Try again later, or download the latest version manually.',
},
ko: {
installFailedTitle: 'Could not install update',
installFailedFallback: 'Try again later.',
installManualFallback: 'Try again later, or download the latest version manually.',
activeTasksTitle: 'Tasks are still running',
activeTasksDescription: 'Tasks are still running. Updating will interrupt them. Continue?',
activeTasksConfirm: 'Update anyway',
activeTasksCancel: 'Cancel',
retryFailedTitle: 'Could not retry update download',
retryFailedFallback: 'Try again later, or download the latest version manually.',
},
} satisfies UiCatalog<AppUpdateCopy>;

export function getAppUpdateCopy(locale: UiLocale): AppUpdateCopy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ const PROVIDER_SETTINGS_COPY = {
'zh-CN': zhCopy,
'zh-TW': zhTwCopy,
en: enCopy,
ko: enCopy,
} satisfies UiCatalog<ProviderSettingsCopy>;

export function getProviderSettingsCopy(locale: UiLocale): ProviderSettingsCopy {
Expand Down
46 changes: 46 additions & 0 deletions apps/desktop/src/renderer/locales/agent-graph-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,52 @@ const AGENT_GRAPH_PANEL_COPY = {
})[status],
wait: waitReasonEn,
},
ko: {
title: 'Agent Graph',
loading: 'Loading graph state…',
retry: 'Retry',
collapse: 'Collapse Agent Graph',
expand: 'Expand Agent Graph',
dismiss: 'Dismiss Agent Graph',
stop: 'Stop graph',
stopping: 'Stopping…',
stopFailed: 'Could not stop the graph. Try again.',
loadFailed: 'Could not refresh graph state.',
openSession: 'Open child task',
operators: 'Operators',
selectedResults: 'Selected results',
epoch: 'Graph run',
currentEpoch: 'Current',
historicalEpoch: 'History (read-only)',
cappedEpochs: (count) => `Showing the newest ${count} runs`,
noOperators: 'Waiting for the main agent to create an operator…',
hiddenOperators: (count) => `${count} more operator${count === 1 ? '' : 's'}`,
progress: (settled, total, hasOmitted) =>
hasOmitted ? `${settled}/${total} visible settled` : `${settled}/${total} settled`,
status: (status) =>
({
empty: 'Awaiting schedule',
active: 'Running',
closing: 'Finishing',
waiting: 'Waiting',
stopped: 'Stopped',
failed: 'Failed',
completed: 'Completed',
})[status],
operatorStatus: (status) =>
({
not_started: 'Not started',
waiting: 'Waiting',
runnable: 'Runnable',
running: 'Running',
blocked: 'Blocked',
completed: 'Completed',
failed: 'Failed',
aborted: 'Aborted',
cancelled: 'Cancelled',
})[status],
wait: waitReasonEn,
},
} satisfies UiCatalog<AgentGraphPanelCopy>;

export function getAgentGraphPanelCopy(locale: UiLocale): AgentGraphPanelCopy {
Expand Down
36 changes: 36 additions & 0 deletions apps/desktop/src/renderer/locales/artifact-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,42 @@ const ARTIFACT_COPY = {
unsupported: 'Unsupported preview', name: 'Name', unnamed: '(unnamed)', type: 'Type', size: 'Size', openInFinder: 'Show in Finder', loadingImage: 'Loading image preview…',
},
},
ko: {
pane: {
refreshFailed: 'Failed to refresh generated files', openFailed: 'Could not show generated file in Finder', copyFailed: 'Copy failed',
readTextFailed: 'Could not read the generated file as text.', copied: 'Generated file text copied', saved: 'Generated file saved as', saveFailed: 'Save as failed',
fallbackName: 'generated file', deleteTitle: (name) => `Delete "${name}"`, deleteDescription: 'Soft delete: mark this record as deleted and keep the file recoverable for 6 hours.',
delete: 'Delete', deleteReadOnly: 'Delete (read-only file)', cancel: 'Cancel', deleted: (name) => `Deleted ${name}`, deleteFailed: (name) => `Failed to delete ${name}`, panelAria: 'Generated file preview panel',
listLoadFailed: 'Failed to load generated files', retrying: 'Retrying…', retry: 'Retry', listAria: 'Generated files', deletedBadge: 'Deleted',
previewNamed: (name) => `Preview ${name}`, empty: 'No generated files', emptyHint: 'Files generated by the assistant appear here.',
back: 'Back to generated files', moreActions: (name) => `More actions for ${name}`,
openInFinder: 'Show in Finder', saveAs: 'Save as', copy: 'Copy',
saveFailures: { not_found: 'The generated file does not exist.', not_allowed: 'The generated file failed the path safety check.', deleted: 'Deleted generated files cannot be saved.', write_failed: 'The destination is not writable.', default: 'Could not save the generated file.' },
actionFailed: 'The generated file action failed. Try again later.',
},
preview: {
loadingFile: 'Loading file preview…', loadingDiff: 'Loading diff preview…', loadingHtml: 'Loading HTML preview…',
externalLinks: (count) => `External links are disabled in this preview · ${count} ${count === 1 ? 'link' : 'links'}`, frameTitle: (name) => `Generated file preview · ${name}`,
loadingPdf: 'Loading PDF preview…', pdfFallback: 'If your browser has no built-in PDF viewer, use “Show in Finder” in the More menu.',
rendered: 'Preview', source: 'Source', previewLimited: (limit) => `Showing the first ${limit}. Use the More menu to open or save the complete file.`,
renderLimited: (limit, lines) => `To stay responsive, rich preview is limited to the first ${limit} and ${lines} lines. The complete source remains available.`,
highlightLimited: (limit, lines) => `To stay responsive, syntax highlighting is limited to the first ${limit} and ${lines} lines; the rest is plain text.`,
diffLinesLimited: (count) => `${count} more lines are hidden to keep the preview responsive.`,
readFailed: { title: 'Could not read generated file', description: 'The file may have been deleted externally. Use “Show in Finder” in the More menu to check its location.' },
notAllowed: { title: 'Could not read generated file', description: 'The path safety check failed because the file is no longer inside the allowed generated-files directory.' },
tooLarge: (bytes) => ({ title: 'File exceeds preview size', description: `${bytes} bytes exceeds the text preview limit. Use the More menu to open or save the complete file.` }),
deleted: { title: 'This generated file was deleted', description: 'The preview has stopped. Use “Show in Finder” to inspect the original file.' },
unsupportedMime: { title: 'Unsupported file type', description: 'This generated file’s MIME type is not allowed for inline preview. Use “Show in Finder” or “Save as”.' },
},
registry: {
kindDisallowed: { title: 'This type cannot be previewed here', description: 'This generated file cannot be previewed in the panel. Use “Show in Finder”.' },
mimeDisallowed: { title: 'Preview format not supported', description: 'The MIME type was recognized, but previews currently support only PNG / JPEG / GIF / WebP / AVIF.' },
unknownType: { title: 'Could not identify file type', description: 'The file has no MIME metadata and its extension did not match. Use “Show in Finder”.' },
oversize: { title: 'File too large to preview', description: 'Files over 2 MB are not expanded here to avoid loading large images into memory.' },
readFailed: { title: 'Failed to load preview', description: 'The file could not be read. It may have been deleted, moved, or blocked by permissions. Use “Show in Finder” to inspect it.' },
unsupported: 'Unsupported preview', name: 'Name', unnamed: '(unnamed)', type: 'Type', size: 'Size', openInFinder: 'Show in Finder', loadingImage: 'Loading image preview…',
},
}
} satisfies UiCatalog<ArtifactCopy>;

export function getArtifactCopy(locale: UiLocale): ArtifactCopy {
Expand Down
Loading