diff --git a/.gitattributes b/.gitattributes index 399e80991141..c2dc09be6a22 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,5 @@ /skill-guides/*.md text eol=lf /skills/*/SKILL.md text eol=lf /src/cli/bundled-skill-guides.ts text eol=lf +/src/renderer/src/i18n/locales/zh.json text eol=lf +/src/renderer/src/i18n/locales/zh-TW.json text eol=lf diff --git a/config/scripts/bootstrap-locale-catalog.mjs b/config/scripts/bootstrap-locale-catalog.mjs index 05739b4da9ce..125b60b78302 100644 --- a/config/scripts/bootstrap-locale-catalog.mjs +++ b/config/scripts/bootstrap-locale-catalog.mjs @@ -126,6 +126,13 @@ function parseLocaleArg(argv) { } export async function main(root = process.cwd(), locale = parseLocaleArg(process.argv)) { + if (locale === 'zh') { + console.error( + 'Simplified Chinese is human-reviewed only. Add a zh key override, then run `pnpm run repair:locale-catalog -- --locale zh`.' + ) + return 1 + } + const config = LOCALE_CONFIG[locale] if (!config) { console.error( diff --git a/config/scripts/bootstrap-locale-catalog.test.mjs b/config/scripts/bootstrap-locale-catalog.test.mjs new file mode 100644 index 000000000000..0055a0d7420e --- /dev/null +++ b/config/scripts/bootstrap-locale-catalog.test.mjs @@ -0,0 +1,16 @@ +import { describe, expect, it, vi } from 'vitest' + +import { main } from './bootstrap-locale-catalog.mjs' + +describe('bootstrap-locale-catalog', () => { + it('rejects machine translation for Simplified Chinese', async () => { + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) + + await expect(main('/path/that/must/not/be-read', 'zh')).resolves.toBe(1) + expect(consoleError).toHaveBeenCalledWith( + expect.stringContaining('Simplified Chinese is human-reviewed only') + ) + + consoleError.mockRestore() + }) +}) diff --git a/config/scripts/generate-zh-tw-catalog.mjs b/config/scripts/generate-zh-tw-catalog.mjs new file mode 100644 index 000000000000..b406fe4be91e --- /dev/null +++ b/config/scripts/generate-zh-tw-catalog.mjs @@ -0,0 +1,84 @@ +import fs from 'node:fs/promises' +import path from 'node:path' +import { pathToFileURL } from 'node:url' +import process from 'node:process' + +import OpenCC from 'opencc-js' + +const SOURCE_CATALOG_PATH = path.join('src', 'renderer', 'src', 'i18n', 'locales', 'zh.json') +const OUTPUT_CATALOG_PATH = path.join('src', 'renderer', 'src', 'i18n', 'locales', 'zh-TW.json') +const INTERPOLATION_PART_RE = /(\{\{[^{}]+\}\})/g +const WHOLE_INTERPOLATION_RE = /^\{\{[^{}]+\}\}$/ +const toTraditionalChinese = OpenCC.Converter({ from: 'cn', to: 'tw' }) + +export function convertSimplifiedText(value) { + // Why: interpolation identifiers are an application contract, not user-visible copy. + return value + .split(INTERPOLATION_PART_RE) + .map((part) => (WHOLE_INTERPOLATION_RE.test(part) ? part : toTraditionalChinese(part))) + .join('') +} + +export function convertSimplifiedCatalog(value) { + if (typeof value === 'string') { + return convertSimplifiedText(value) + } + if (Array.isArray(value)) { + return value.map((item) => convertSimplifiedCatalog(item)) + } + if (typeof value === 'object' && value !== null) { + return Object.fromEntries( + Object.entries(value).map(([key, child]) => [key, convertSimplifiedCatalog(child)]) + ) + } + return value +} + +export function serializeCatalog(catalog) { + return `${JSON.stringify(catalog, null, 2)}\n` +} + +async function expectedCatalogText(root) { + const source = JSON.parse(await fs.readFile(path.join(root, SOURCE_CATALOG_PATH), 'utf8')) + return serializeCatalog(convertSimplifiedCatalog(source)) +} + +export async function main(root = process.cwd(), argv = process.argv.slice(2)) { + const write = argv.includes('--write') + const check = argv.includes('--check') + if (write === check) { + console.error('Pass exactly one of --write or --check.') + return 1 + } + + const outputPath = path.join(root, OUTPUT_CATALOG_PATH) + const expected = await expectedCatalogText(root) + + if (write) { + await fs.writeFile(outputPath, expected, 'utf8') + console.log(`Generated ${OUTPUT_CATALOG_PATH} from ${SOURCE_CATALOG_PATH}.`) + return 0 + } + + let actual + try { + actual = await fs.readFile(outputPath, 'utf8') + } catch (error) { + if (error?.code !== 'ENOENT') { + throw error + } + } + + if (actual !== expected) { + console.error(`${OUTPUT_CATALOG_PATH} is stale or missing.`) + console.error('Run `pnpm run generate:zh-tw-catalog` and commit the generated catalog.') + return 1 + } + + console.log(`Verified ${OUTPUT_CATALOG_PATH} is generated from ${SOURCE_CATALOG_PATH}.`) + return 0 +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + process.exit(await main()) +} diff --git a/config/scripts/generate-zh-tw-catalog.test.mjs b/config/scripts/generate-zh-tw-catalog.test.mjs new file mode 100644 index 000000000000..82a075b8d07c --- /dev/null +++ b/config/scripts/generate-zh-tw-catalog.test.mjs @@ -0,0 +1,60 @@ +import fs from 'node:fs/promises' +import path from 'node:path' + +import { describe, expect, it } from 'vitest' + +import { + convertSimplifiedCatalog, + convertSimplifiedText, + serializeCatalog +} from './generate-zh-tw-catalog.mjs' + +const LOCALES_DIR = path.join('src', 'renderer', 'src', 'i18n', 'locales') +const PLACEHOLDER_RE = /\{\{[^}]+\}\}/g + +function flattenStrings(value, prefix = '', entries = new Map()) { + if (typeof value === 'string') { + entries.set(prefix, value) + return entries + } + if (typeof value !== 'object' || value === null || Array.isArray(value)) { + return entries + } + for (const [key, child] of Object.entries(value)) { + flattenStrings(child, prefix ? `${prefix}.${key}` : key, entries) + } + return entries +} + +async function readCatalog(fileName) { + return JSON.parse(await fs.readFile(path.join(LOCALES_DIR, fileName), 'utf8')) +} + +describe('Traditional Chinese catalog generation', () => { + it('uses deterministic OpenCC conversion while preserving interpolation identifiers', () => { + expect(convertSimplifiedText('汉语 {{用户}}')).toBe('漢語 {{用户}}') + expect(convertSimplifiedCatalog({ label: '设置', count: 2 })).toEqual({ + label: '設置', + count: 2 + }) + }) + + it('matches the committed catalog exactly', async () => { + const simplified = await readCatalog('zh.json') + const traditionalText = await fs.readFile(path.join(LOCALES_DIR, 'zh-TW.json'), 'utf8') + + expect(serializeCatalog(convertSimplifiedCatalog(simplified))).toBe(traditionalText) + }) + + it('keeps catalog keys and placeholders identical to Simplified Chinese', async () => { + const simplifiedEntries = flattenStrings(await readCatalog('zh.json')) + const traditionalEntries = flattenStrings(await readCatalog('zh-TW.json')) + + expect([...traditionalEntries.keys()]).toEqual([...simplifiedEntries.keys()]) + for (const [key, simplifiedValue] of simplifiedEntries) { + expect(traditionalEntries.get(key)?.match(PLACEHOLDER_RE) ?? []).toEqual( + simplifiedValue.match(PLACEHOLDER_RE) ?? [] + ) + } + }) +}) diff --git a/config/scripts/locale-cjk-latin-spaced-terms.mjs b/config/scripts/locale-cjk-latin-spaced-terms.mjs index 85f460a49801..f12e0d676070 100644 --- a/config/scripts/locale-cjk-latin-spaced-terms.mjs +++ b/config/scripts/locale-cjk-latin-spaced-terms.mjs @@ -29,11 +29,22 @@ export const CJK_LATIN_SPACED_TERMS = [ 'Claude Code', 'Codex', 'Gemini', + 'Hermes', 'Kimi', 'OpenCode', 'Orca', 'Cursor', 'Bitbucket', + 'OpenAI', + 'Google', + 'Chrome', + 'DuckDuckGo', + 'Atlassian', + 'macOS', + 'Ubuntu', + 'Debian', + 'WebGL', + 'SCSS', 'Tailscale', 'Kagi', 'SSH', diff --git a/config/scripts/locale-phrase-fixes.mjs b/config/scripts/locale-phrase-fixes.mjs index fec68ef8b5d7..d2e36d445144 100644 --- a/config/scripts/locale-phrase-fixes.mjs +++ b/config/scripts/locale-phrase-fixes.mjs @@ -1,4 +1,5 @@ import { KO_PHRASE_FIXES_ROUND4 } from './locale-ko-phrase-fixes.mjs' +import { ZH_HUMAN_PHRASE_FIXES } from './locale-zh-phrase-fixes-human.mjs' import { ZH_PHRASE_FIXES_ROUND5 } from './locale-zh-phrase-fixes-round5.mjs' import { JA_PHRASE_FIXES } from './locale-ja-phrase-fixes.mjs' @@ -131,15 +132,15 @@ export const LOCALE_PHRASE_FIXES = { ...KO_PHRASE_FIXES_ROUND4 ], zh: [ - { pattern: /客服人员/g, replacement: '代理', whenEnIncludes: 'agent' }, + { pattern: /客服人员/g, replacement: '智能体', whenEnIncludes: 'agent' }, { pattern: /会议/g, replacement: '会话', whenEnIncludes: 'session' }, { pattern: /港口/g, replacement: '端口', whenEnIncludes: 'ort' }, { pattern: /公关/g, replacement: 'PR', whenEnIncludes: 'PR' }, { pattern: /虎鲸:\/\//g, replacement: 'orca://', whenEnIncludes: 'orca://' }, - { pattern: /代理商/g, replacement: '代理', whenEnIncludes: 'agent' }, - { pattern: /智能体/g, replacement: '代理', whenEnIncludes: 'agent' }, + { pattern: /代理商/g, replacement: '智能体', whenEnIncludes: 'agent' }, + { pattern: /代理/g, replacement: '智能体', whenEnMatches: /\bagents?\b/i }, { pattern: /分支机构/g, replacement: '分支', whenEnIncludes: 'ranch' }, - { pattern: /座席/g, replacement: '代理', whenEnIncludes: 'agent' }, + { pattern: /座席/g, replacement: '智能体', whenEnIncludes: 'agent' }, { pattern: /汽车/g, replacement: '自动', whenEnIncludes: 'Auto' }, { pattern: /清爽/g, replacement: '刷新中', whenEnIncludes: 'Refreshing' }, { pattern: /瓦斯尔/g, replacement: 'WSL', whenEnIncludes: 'wsl' }, @@ -157,7 +158,7 @@ export const LOCALE_PHRASE_FIXES = { { pattern: /电脑使用/g, replacement: '计算机控制', whenEnIncludes: 'Computer Use' }, { pattern: /快捷方式/g, replacement: '快捷键', whenEnIncludes: 'Shortcuts' }, { pattern: /入职清单/g, replacement: '入门清单', whenEnIncludes: 'Onboarding checklist' }, - { pattern: /发射代理/g, replacement: '启动代理', whenEnIncludes: 'Launch agent' }, + { pattern: /发射代理/g, replacement: '启动智能体', whenEnIncludes: 'Launch agent' }, { pattern: /地位/g, replacement: '状态', whenEnIncludes: 'Status' }, { pattern: /受让人/g, replacement: '负责人', whenEnIncludes: 'assignee' }, { pattern: /开放工作区/g, replacement: '打开工作区', whenEnIncludes: 'Open workspace' }, @@ -376,6 +377,7 @@ export const LOCALE_PHRASE_FIXES = { { pattern: /中性的/g, replacement: 'Neutral', whenEnIncludes: 'Neutral' }, { pattern: /破坏性的/g, replacement: 'destructive', whenEnIncludes: 'destructive' }, { pattern: /注解/g, replacement: '批注', whenEnIncludes: 'Annotation' }, + ...ZH_HUMAN_PHRASE_FIXES, ...ZH_PHRASE_FIXES_ROUND5 ], ja: JA_PHRASE_FIXES, diff --git a/config/scripts/locale-translation-policy.mjs b/config/scripts/locale-translation-policy.mjs index c4991ced9d2c..2a3e278a0e6f 100644 --- a/config/scripts/locale-translation-policy.mjs +++ b/config/scripts/locale-translation-policy.mjs @@ -4,6 +4,7 @@ import { LOCALE_KEY_OVERRIDES } from './locale-key-overrides.mjs' import { LOCALE_PHRASE_FIXES } from './locale-phrase-fixes.mjs' import { SEARCH_KEYWORD_OVERRIDES } from './locale-search-keyword-overrides.mjs' import { LOCALE_VALUE_OVERRIDES } from './locale-value-overrides.mjs' +import { ZH_HUMAN_KEY_OVERRIDES } from './locale-zh-human-key-overrides.mjs' export { LOCALE_KEY_OVERRIDES } from './locale-key-overrides.mjs' export { LOCALE_PHRASE_FIXES } from './locale-phrase-fixes.mjs' @@ -236,24 +237,8 @@ export const BRAND_MISTRANSLATIONS = { Linear: ['线性', '线形'], Jira: ['吉拉'], Tailscale: ['尾鳞', '尾鱗'], - Agent: ['代理', '智能体'], - Agents: ['代理', '智能体'], - agent: ['代理', '智能体'], - agents: ['代理', '智能体'], - Commit: ['提交'], - Commits: ['提交'], - commit: ['提交'], - commits: ['提交'], Markdown: ['降价'], markdown: ['降价'], - Repo: ['存储库', '仓库', '回购协议', '回购'], - Repos: ['存储库', '仓库', '回购协议', '回购'], - repo: ['存储库', '仓库', '回购协议', '回购'], - repos: ['存储库', '仓库', '回购协议', '回购'], - Terminal: ['终端', '端子'], - Terminals: ['终端', '端子'], - terminal: ['终端', '端子'], - terminals: ['终端', '端子'], Bash: ['重击'], PowerShell: ['电源外壳'], REST: ['休息'], @@ -457,6 +442,16 @@ function applyPhraseFixes(enValue, localeValue, locale) { } export function repairTranslatedValue({ key, enValue, localeValue, locale }) { + const humanKeyOverride = locale === 'zh' ? ZH_HUMAN_KEY_OVERRIDES[key]?.zh : undefined + if (humanKeyOverride) { + // Human-reviewed Simplified Chinese is the final copy, not another MT input to normalize. + return humanKeyOverride + } + + if (isEnglishOnlyKey(key)) { + return enValue + } + const keyOverride = LOCALE_KEY_OVERRIDES[key]?.[locale] if (keyOverride) { // Why: exact key overrides can still carry stale MT output, so glossary repairs remain the final gate. diff --git a/config/scripts/locale-translation-policy.test.mjs b/config/scripts/locale-translation-policy.test.mjs index 1722f8f946ee..974e2907dcc5 100644 --- a/config/scripts/locale-translation-policy.test.mjs +++ b/config/scripts/locale-translation-policy.test.mjs @@ -267,7 +267,7 @@ describe('locale-translation-policy', () => { localeValue: '未已检测代理', locale: 'zh' }) - ).toBe('未检测到 agents') + ).toBe('未检测到智能体') expect( repairTranslatedValue({ key: 'auto.components.skills.SkillsPage.38e0951c3a', @@ -275,7 +275,7 @@ describe('locale-translation-policy', () => { localeValue: '代理技巧', locale: 'zh' }) - ).toBe('Agent 技能') + ).toBe('智能体技能') expect( repairTranslatedValue({ key: 'auto.components.settings.appearance.search.9ae151b26b', @@ -522,7 +522,7 @@ describe('locale-translation-policy', () => { localeValue: '排队', locale: 'zh' }) - ).toBe('内联') + ).toBe('行内') expect( repairTranslatedValue({ key: 'auto.components.github.pr.merge.state.bf5e4c6c92', @@ -546,7 +546,7 @@ describe('locale-translation-policy', () => { localeValue: '玩', locale: 'zh' }) - ).toBe('Play') + ).toBe('播放') expect( repairTranslatedValue({ key: 'auto.components.right.sidebar.SourceControlAgentActionDialogForm.1bc0bdbb5e', diff --git a/config/scripts/locale-translation-policy.zh-human.test.mjs b/config/scripts/locale-translation-policy.zh-human.test.mjs new file mode 100644 index 000000000000..9f2b8e09a3f6 --- /dev/null +++ b/config/scripts/locale-translation-policy.zh-human.test.mjs @@ -0,0 +1,139 @@ +import fs from 'node:fs' + +import { describe, expect, it } from 'vitest' + +import { ZH_HUMAN_KEY_OVERRIDES } from './locale-zh-human-key-overrides.mjs' +import { repairTranslatedValue } from './locale-translation-policy.mjs' + +const enCatalog = JSON.parse( + fs.readFileSync(new URL('../../src/renderer/src/i18n/locales/en.json', import.meta.url), 'utf8') +) +const zhCatalog = JSON.parse( + fs.readFileSync(new URL('../../src/renderer/src/i18n/locales/zh.json', import.meta.url), 'utf8') +) + +function getCatalogValue(key) { + return key.split('.').reduce((value, part) => value?.[part], enCatalog) +} + +function interpolationVariables(value) { + return [...value.matchAll(/{{\s*([^},\s]+)[^}]*}}/g)].map((match) => match[1]).sort() +} + +function flattenStrings(value, prefix = '', entries = new Map()) { + if (typeof value === 'string') { + entries.set(prefix, value) + } else if (value && typeof value === 'object' && !Array.isArray(value)) { + for (const [key, child] of Object.entries(value)) { + flattenStrings(child, prefix ? `${prefix}.${key}` : key, entries) + } + } + return entries +} + +function isTechnicalEnglishLiteral(key, value) { + return ( + key.startsWith('auto.lib.agent.catalog.') || + key.startsWith('auto.lib.open.in.app.catalog.') || + value === 'Español' || + /^(?:https?:\/\/|orca:\/\/|\/|~\/|git@|# )/.test(value) || + ['.', '_', '@', '['].some((prefix) => value.startsWith(prefix)) || + /^(?:git|orca|pnpm|npm|gh|glab)\s/.test(value) || + /^[A-Z][A-Z0-9_]+$/.test(value) || + (!value.includes(' ') && value.includes('/')) || + value.includes(' ') || + (value.includes('>') && /^[.#a-z]/.test(value)) || + (value.length > 80 && /[{};]/.test(value)) + ) +} + +describe('human-reviewed Simplified Chinese policy', () => { + it('keeps human full-key copy authoritative', () => { + expect( + repairTranslatedValue({ + key: 'auto.components.stats.StatsPane.1c96f433e2', + enValue: 'Time agents worked', + localeValue: '代理工作时间', + locale: 'zh' + }) + ).toBe('智能体工作时长') + expect( + repairTranslatedValue({ + key: 'auto.components.right.sidebar.SourceControl.37a81f29ad', + enValue: 'Generating commit message. Click to stop.', + localeValue: '生成 commit 消息。单击停止。', + locale: 'zh' + }) + ).toBe('正在生成提交信息。点击可停止。') + }) + + it('translates generic development concepts while preserving product catalogs', () => { + const repair = (key, enValue, localeValue) => + repairTranslatedValue({ key, enValue, localeValue, locale: 'zh' }) + + expect(repair('auto.components.example.agent', 'Agent', '代理')).toBe('智能体') + expect(repair('auto.components.example.terminal', 'Terminal', '终端')).toBe('终端') + expect(repair('auto.components.example.commit', 'Commit', '提交')).toBe('提交') + expect(repair('auto.components.example.repo', 'Repo', '仓库')).toBe('仓库') + expect(repair('auto.lib.agent.catalog.example', 'Agent', '代理')).toBe('Agent') + }) + + it('keeps operating-system agents distinct from AI agents', () => { + const repair = (enValue) => + repairTranslatedValue({ + key: 'auto.components.example', + enValue, + localeValue: '代理', + locale: 'zh' + }) + + expect(repair('AI agent')).toBe('智能体') + expect(repair('SSH agent')).toBe('代理') + expect(repair('User agent')).toBe('代理') + }) + + it('normalizes manually approved terminology and technical names', () => { + const repair = (enValue, localeValue) => + repairTranslatedValue({ key: 'auto.components.example', enValue, localeValue, locale: 'zh' }) + + expect(repair('Repository settings', '存储库设置')).toBe('仓库设置') + expect(repair('Sparse checkout', '稀疏结账')).toBe('稀疏检出') + expect(repair('Hosted review', '托管评论')).toBe('托管评审') + expect(repair('Browser tabs', '浏览器选项卡')).toBe('浏览器标签页') + expect(repair('Import cookies', '导入 cookie')).toBe('导入 Cookie') + expect(repair('Connect your account', '连接您的账户')).toBe('连接你的账户') + expect(repair('You can retry', '您可以重试')).toBe('你可以重试') + expect(repair('OpenAI', '开放伊')).toBe('OpenAI') + expect(repair('Hermes automation', '爱马仕自动化')).toBe('Hermes 自动化') + }) + + it('keeps every human key valid and preserves interpolation variables', () => { + for (const [key, override] of Object.entries(ZH_HUMAN_KEY_OVERRIDES)) { + const source = getCatalogValue(key) + expect(source, key).toEqual(expect.any(String)) + expect(override.zh, key).toEqual(expect.any(String)) + expect(interpolationVariables(override.zh), key).toEqual(interpolationVariables(source)) + } + }) + + it('does not leave ordinary English prose in the repaired Chinese catalog', () => { + const englishEntries = flattenStrings(enCatalog) + const chineseEntries = flattenStrings(zhCatalog) + const untranslated = [] + + for (const [key, enValue] of englishEntries) { + const repaired = repairTranslatedValue({ + key, + enValue, + localeValue: chineseEntries.get(key), + locale: 'zh' + }) + const wordCount = enValue.match(/[A-Za-z][A-Za-z'-]*/g)?.length ?? 0 + if (repaired === enValue && wordCount >= 4 && !isTechnicalEnglishLiteral(key, enValue)) { + untranslated.push(`${key}: ${enValue}`) + } + } + + expect(untranslated).toEqual([]) + }) +}) diff --git a/config/scripts/locale-translation-policy.zh-round5.test.mjs b/config/scripts/locale-translation-policy.zh-round5.test.mjs index 6e451c6d89b5..d53f91e685ca 100644 --- a/config/scripts/locale-translation-policy.zh-round5.test.mjs +++ b/config/scripts/locale-translation-policy.zh-round5.test.mjs @@ -86,7 +86,7 @@ describe('locale-translation-policy zh round 5', () => { ).toBe('集成') }) - it('keeps Terminal as a product surface term', () => { + it('uses natural Chinese for terminal surfaces', () => { expect( repairTranslatedValue({ key: 'auto.components.settings.Settings.3de4bbb841', @@ -94,7 +94,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '终端', locale: 'zh' }) - ).toBe('Terminal') + ).toBe('终端') expect( repairTranslatedValue({ key: 'auto.components.feature.wall.BrowserAnimatedVisual.04096318ab', @@ -102,7 +102,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '终端 1', locale: 'zh' }) - ).toBe('Terminal 1') + ).toBe('终端 1') expect( repairTranslatedValue({ key: 'auto.components.agent.AgentCombobox.986f946354', @@ -110,7 +110,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '空白端子', locale: 'zh' }) - ).toBe('空白 Terminal') + ).toBe('空白终端') expect( repairTranslatedValue({ key: 'auto.components.terminal.pane.TerminalContextMenu.20e565d865', @@ -118,7 +118,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '分体式端子右', locale: 'zh' }) - ).toBe('向右拆分 Terminal') + ).toBe('向右拆分终端') expect( repairTranslatedValue({ key: 'auto.components.settings.TerminalAppearanceSection.abcb4dd019', @@ -126,7 +126,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '终端Cursor', locale: 'zh' }) - ).toBe('Terminal Cursor') + ).toBe('终端光标') expect( repairTranslatedValue({ key: 'auto.components.terminal.FloatingTerminalPanel.3215fc73e9', @@ -134,10 +134,10 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '新Terminal', locale: 'zh' }) - ).toBe('新 Terminal') + ).toBe('新建终端') }) - it('keeps workflow terms in English', () => { + it('uses natural Chinese for generic workflow terms', () => { expect( repairTranslatedValue({ key: 'auto.components.sidebar.SidebarNav.9c95e1ce91', @@ -145,7 +145,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '代理', locale: 'zh' }) - ).toBe('Agents') + ).toBe('智能体') expect( repairTranslatedValue({ key: 'auto.components.GitHubItemDialog.28986b3747', @@ -153,7 +153,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '已启动 AI 代理处理失败的检查。', locale: 'zh' }) - ).toBe('已启动 AI agent 处理失败的检查。') + ).toBe('已启动智能体处理失败的检查。') expect( repairTranslatedValue({ key: 'auto.components.LinearIssueMarkdownDescriptionEditor.d9c47069ef', @@ -169,7 +169,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '描述(可选,降价)', locale: 'zh' }) - ).toBe('描述(可选,markdown)') + ).toBe('描述(可选,Markdown)') expect( repairTranslatedValue({ key: 'auto.components.sidebar.local.base.ref.suggestion.toast.commits', @@ -177,7 +177,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '次提交', locale: 'zh' }) - ).toBe('commits') + ).toBe('提交') expect( repairTranslatedValue({ key: 'auto.store.slices.worktrees.d1d78a7baa', @@ -187,7 +187,7 @@ describe('locale-translation-policy zh round 5', () => { 'Git 无法安全删除分支“{{value0}}”{{value1}},因此 Orca 保留它以避免丢失本地提交。', locale: 'zh' }) - ).toBe('Git 无法安全删除分支“{{value0}}”{{value1}},因此 Orca 保留它以避免丢失本地 commits。') + ).toBe('Git 无法安全删除分支“{{value0}}”{{value1}},因此 Orca 保留了该分支,以免丢失本地提交。') }) it('does not confuse proxy copy with Agent terminology', () => { @@ -218,7 +218,7 @@ describe('locale-translation-policy zh round 5', () => { ).toBe('no_proxy') }) - it('keeps repo terminology in English', () => { + it('uses repository terminology consistently', () => { expect( repairTranslatedValue({ key: 'auto.components.workspace.cleanup.WorkspaceCleanupDialog.0b1766738a', @@ -226,7 +226,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '回购协议', locale: 'zh' }) - ).toBe('Repo') + ).toBe('仓库') expect( repairTranslatedValue({ key: 'auto.components.sidebar.add.repo.local.start.actions.fb4fc5380e', @@ -234,7 +234,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '本地项目、Git 存储库或包含多个存储库的文件夹', locale: 'zh' }) - ).toBe('本地项目、Git repo 或包含多个 repos 的文件夹') + ).toBe('本地项目、Git 仓库或包含多个仓库的文件夹') }) it('keeps product, provider, code, and shell tokens untranslated', () => { @@ -269,7 +269,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: 'git 重击', locale: 'zh' }) - ).toBe('git bash') + ).toBe('Git Bash') expect( repairTranslatedValue({ key: 'auto.components.tab.bar.TabBar.2148f65e04', @@ -320,7 +320,7 @@ describe('locale-translation-policy zh round 5', () => { localeValue: '显示Claude Token 和成本使用情况。', locale: 'zh' }) - ).toBe('显示 Claude Token 和成本使用情况。') + ).toBe('显示当前工作区的 Claude 令牌用量和费用。') expect( repairTranslatedValue({ key: 'auto.components.feature.wall.ComputerUseAnimatedVisual.94787f01f8', diff --git a/config/scripts/locale-value-overrides.mjs b/config/scripts/locale-value-overrides.mjs index e237bca76b09..8d74570cf7eb 100644 --- a/config/scripts/locale-value-overrides.mjs +++ b/config/scripts/locale-value-overrides.mjs @@ -1,5 +1,6 @@ import { JA_VALUE_OVERRIDES } from './locale-ja-value-overrides.mjs' import { KO_VALUE_OVERRIDES } from './locale-ko-value-overrides.mjs' +import { ZH_HUMAN_VALUE_OVERRIDES } from './locale-zh-human-value-overrides.mjs' import { ZH_VALUE_OVERRIDES } from './locale-zh-value-overrides.mjs' export const LOCALE_VALUE_OVERRIDES = { @@ -305,7 +306,7 @@ export const LOCALE_VALUE_OVERRIDES = { teams: '团队', passing: '通过', 'opened this issue': '创建了此议题', - Open: '进行中', + Open: '打开', 'Join Discord': '加入 Discord', 'Pull request merged': '拉取请求已合并', 'Agent Skills': '代理技能', @@ -371,7 +372,8 @@ export const LOCALE_VALUE_OVERRIDES = { 'Fix checks prompt is empty. Update Source Control AI settings.': '检查提示为空。请更新源代码管理 AI 设置。', Question: '疑问', - ...ZH_VALUE_OVERRIDES + ...ZH_VALUE_OVERRIDES, + ...ZH_HUMAN_VALUE_OVERRIDES }, ja: { Save: '保存', diff --git a/config/scripts/locale-zh-human-key-overrides.mjs b/config/scripts/locale-zh-human-key-overrides.mjs new file mode 100644 index 000000000000..2c8dc498614c --- /dev/null +++ b/config/scripts/locale-zh-human-key-overrides.mjs @@ -0,0 +1,16 @@ +import { ZH_MANUAL_BROWSER_KEY_OVERRIDES } from './locale-zh-manual-browser-key-overrides.mjs' +import { ZH_MANUAL_COLLABORATION_KEY_OVERRIDES } from './locale-zh-manual-collaboration-key-overrides.mjs' +import { ZH_MANUAL_INTERPOLATED_COPY_KEY_OVERRIDES } from './locale-zh-manual-interpolated-copy-key-overrides.mjs' +import { ZH_MANUAL_PRODUCT_SURFACE_KEY_OVERRIDES } from './locale-zh-manual-product-surface-key-overrides.mjs' +import { ZH_MANUAL_SETTINGS_KEY_OVERRIDES } from './locale-zh-manual-settings-key-overrides.mjs' +import { ZH_MANUAL_WORKSPACE_KEY_OVERRIDES } from './locale-zh-manual-workspace-key-overrides.mjs' + +// Human-reviewed full-key copy is authoritative and must not pass through generic MT repairs. +export const ZH_HUMAN_KEY_OVERRIDES = { + ...ZH_MANUAL_BROWSER_KEY_OVERRIDES, + ...ZH_MANUAL_COLLABORATION_KEY_OVERRIDES, + ...ZH_MANUAL_INTERPOLATED_COPY_KEY_OVERRIDES, + ...ZH_MANUAL_PRODUCT_SURFACE_KEY_OVERRIDES, + ...ZH_MANUAL_SETTINGS_KEY_OVERRIDES, + ...ZH_MANUAL_WORKSPACE_KEY_OVERRIDES +} diff --git a/config/scripts/locale-zh-human-value-overrides.mjs b/config/scripts/locale-zh-human-value-overrides.mjs new file mode 100644 index 000000000000..2254b46145af --- /dev/null +++ b/config/scripts/locale-zh-human-value-overrides.mjs @@ -0,0 +1,250 @@ +// Human-reviewed standalone UI terms and search keywords. +// Why: isolated words give translation models too little context, causing homograph and brand errors. +export const ZH_HUMAN_VALUE_OVERRIDES = { + Agent: '智能体', + Agents: '智能体', + agent: '智能体', + agents: '智能体', + Added: '已添加', + added: '已添加', + args: '参数', + arguments: '参数', + Avatar: '头像', + Amber: '琥珀色', + 'Blank Terminal': '空白终端', + cells: '单元格', + chrome: 'Chrome', + cookies: 'Cookie', + Commit: '提交', + Commits: '提交', + commit: '提交', + commits: '提交', + CPU: 'CPU', + cpu: 'CPU', + custom: '自定义', + Details: '详情', + distro: '发行版', + Finder: 'Finder', + finder: 'Finder', + Found: '已找到', + found: '已找到', + google: 'Google', + GPU: 'GPU', + gpu: 'GPU', + handoff: '接续', + HTML: 'HTML', + i18n: 'i18n', + Local: '本地', + local: '本地', + 'Local project, Git repo, or folder with many repos': '本地项目、Git 仓库或包含多个仓库的文件夹', + macos: 'macOS', + Memory: '内存', + memory: '内存', + node_modules: 'node_modules', + npm: 'npm', + npx: 'npx', + openai: 'OpenAI', + OMP: 'OMP', + omp: 'OMP', + Origin: 'origin', + origin: 'origin', + Package: '软件包', + page: '页面', + profile: '配置文件', + provider: '提供商', + Providers: '提供商', + query: '查询', + recommended: '推荐', + rich: '富文本', + Repo: '仓库', + Repos: '仓库', + repo: '仓库', + repos: '仓库', + SCSS: 'SCSS', + shell: 'Shell', + shortcut: '快捷键', + slug: 'slug', + 'source-control': '源代码管理', + sparse: '稀疏', + Sparse: '稀疏', + stt: 'STT', + tailnet: 'tailnet', + thread: '线程', + Terminal: '终端', + Terminals: '终端', + terminal: '终端', + terminals: '终端', + 'Terminal Cursor': '终端光标', + 'New Terminal': '新建终端', + 'Split Terminal Down': '向下拆分终端', + 'Split Terminal Right': '向右拆分终端', + 'Agent Skills': '智能体技能', + 'Agent skills': '智能体技能', + 'Agent skill': '智能体技能', + 'No agents detected': '未检测到智能体', + Timeout: '超时', + tui: 'TUI', + usage: '用量', + views: '视图', + Views: '视图', + Violet: '紫罗兰色', + Web: 'Web', + webgl: 'WebGL', + Window: '窗口', + window: '窗口', + windows: 'Windows', + workflow: '工作流', + Workflows: '工作流', + ansi: 'ANSI', + atlassian: 'Atlassian', + debian: 'Debian', + duckduckgo: 'DuckDuckGo', + fzf: 'fzf', + gitignore: 'gitignore', + neovim: 'Neovim', + nvim: 'nvim', + pty: 'PTY', + tmux: 'tmux', + ubuntu: 'Ubuntu', + 'arc profile': 'Arc 配置文件', + 'edge profile': 'Edge 配置文件', + 'Task Providers': '任务提供商', + aac: 'AAC', + accessibility: '无障碍', + alpha: 'Alpha', + alt: 'Alt', + apk: 'APK', + arc: 'Arc', + auth: '认证', + app: '应用', + Bar: '竖线', + Base: '基准', + base: '基准', + Block: '块', + Blue: '蓝色', + Box: '方框', + Bundled: '内置', + Change: '更改', + character: '字符', + check: '检查', + checkout: '检出', + Claudino: 'Claudino', + cli: 'CLI', + cmd: 'cmd', + component: '组件', + connection: '连接', + coordination: '协作', + darwin: 'Darwin', + 'directly.': '直接。', + distribution: '发行版', + dock: 'Dock', + edge: 'Edge', + editor: '编辑器', + Emerald: '翠绿色', + eligible: '符合条件', + existing: '现有', + Failed: '失败', + favicon: '网站图标', + fit: '适应', + flac: 'FLAC', + focus: '焦点', + focused: '已聚焦', + Flag: '旗帜', + Force: '强制', + Fresh: '全新', + frozen: '已冻结', + glab: 'glab', + global: '全局', + github: 'GitHub', + gitlab: 'GitLab', + Gone: '已失效', + Grace: '宽限期', + Grep: 'grep', + High: '高', + hold: '按住', + Hold: '按住', + home: '主页', + Home: '主页', + hover: '悬停', + ignored: '已忽略', + instructions: '说明', + international: '国际', + japanese: '日语', + jis: 'JIS', + job: '任务', + 'kebab-case': 'kebab-case', + kill: '强制结束', + kimi: 'Kimi', + Kind: '类型', + landing: '起始页', + launchpad: 'Launchpad', + Less: '更少', + ligature: '连字', + linux: 'Linux', + Low: '低', + m4a: 'M4A', + mac: 'Mac', + manager: '管理器', + margin: '边距', + Markdown: 'Markdown', + markdown: 'Markdown', + message: '消息', + Message: '消息', + moonshot: 'Moonshot', + mouse: '鼠标', + Move: '移动', + mrs: 'MR', + mru: 'MRU', + name: '名称', + native: '原生', + nbformat: 'nbformat', + new_per_run: '每次新建', + No: '否', + none: '无', + None: '无', + off: '关闭', + Off: '关闭', + ogg: 'OGG', + on: '开启', + On: '开启', + osc52: 'OSC 52', + pair: '配对', + PASS: '通过', + pending: '待处理', + Pink: '粉色', + Play: '播放', + prs: 'PR', + posthog: 'PostHog', + Pull: '拉取', + Push: '推送', + Raw: '原始', + readline: 'readline', + Regenerate: '重新生成', + remote: '远程', + Reuse: '重用', + Right: '右', + Rose: '玫瑰红', + sheet: '面板', + Sky: '天蓝色', + sox: 'SoX', + speech: '语音', + streaming: '流式传输', + switch: '切换', + Teal: '青绿色', + top: '顶部', + underline: '下划线', + Unlock: '解锁', + url: 'URL', + verify: '验证', + wav: 'WAV', + weekly: '每周', + whisper: 'Whisper', + wifi: 'Wi-Fi', + wk: '周', + worker: '执行节点', + working: '处理中', + 'wsl.exe': 'wsl.exe', + zed: 'Zed', + Zinc: '锌灰色', + Zoom: '缩放' +} diff --git a/config/scripts/locale-zh-manual-activity-skills-repository-workspace-crash-key-overrides.mjs b/config/scripts/locale-zh-manual-activity-skills-repository-workspace-crash-key-overrides.mjs new file mode 100644 index 000000000000..74749f57899b --- /dev/null +++ b/config/scripts/locale-zh-manual-activity-skills-repository-workspace-crash-key-overrides.mjs @@ -0,0 +1,58 @@ +// Human-reviewed Simplified Chinese for activity, skills, repositories, workspace creation, and crash reporting. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_ACTIVITY_SKILLS_REPOSITORY_WORKSPACE_CRASH_KEY_OVERRIDES = { + // Activity, skills, repositories, workspace creation, and crash reporting. + 'auto.components.activity.ActivityPrototypePage.22b22034bc': { + zh: '活动页面不支持独立终端。' + }, + 'auto.components.activity.ActivityPrototypePage.443690186e': { zh: '调整活动线程列表大小' }, + 'auto.components.activity.ActivityPrototypePage.59b131fbd9': { zh: '将线程标记为未读' }, + 'auto.components.activity.ActivityPrototypePage.a472a14700': { zh: '更多选项' }, + 'auto.components.activity.ActivityPrototypePage.afdc2139a8': { + zh: '智能体终端已关闭。请在此工作区打开新终端以继续。' + }, + 'auto.components.activity.ActivityPrototypePage.d1a88df9a8': { zh: '仅显示未读线程' }, + 'auto.components.activity.ActivityPrototypePage.e3db9892f6': { zh: '暂无活动。' }, + 'auto.components.activity.ActivityTitlebarControls.dc708f3eff': { zh: '关闭智能体' }, + 'auto.components.skills.SkillsPage.0c74e7ff34': { zh: '本地' }, + 'auto.components.skills.SkillsPage.35b9a724a0': { zh: '可用' }, + 'auto.components.skills.SkillsPage.39b6998ddb': { zh: '所有提供方' }, + 'auto.components.skills.SkillsPage.4d177feabd': { zh: '内置' }, + 'auto.components.skills.SkillsPage.571c5818c1': { zh: '主目录' }, + 'auto.components.skills.SkillsPage.995fde8337': { zh: '无法在文件夹中显示技能文件' }, + 'auto.components.skills.SkillsPage.9963dff6d3': { zh: '暂无说明。' }, + 'auto.components.skills.SkillsPage.dc4c3328ee': { zh: '在文件夹中显示' }, + 'auto.components.repo.NestedRepoScanLimitNotice.642a43c139': { zh: '嵌套仓库扫描限制' }, + 'auto.components.repo.RepoCombobox.e7ed739236': { zh: '没有项目或文件夹符合搜索条件。' }, + 'auto.components.repo.repo.icon.0ad395d475': { zh: '方框' }, + 'auto.components.repo.repo.icon.3c5a593bc8': { zh: 'Web' }, + 'auto.components.repo.repo.icon.70bef15d40': { zh: '图层' }, + 'auto.components.repo.repo.icon.787490e9bd': { zh: '软件包' }, + 'auto.components.new.workspace.SmartWorkspaceNameField.2319d87718': { zh: '已合并' }, + 'auto.components.new.workspace.SmartWorkspaceNameField.34ca97bce3': { zh: '“' }, + 'auto.components.new.workspace.SmartWorkspaceNameField.622864b52a': { zh: '未关闭' }, + 'auto.components.new.workspace.SmartWorkspaceNameField.69ce292138': { zh: 'Linear' }, + 'auto.components.new.workspace.SmartWorkspaceNameField.6f07a18604': { zh: '名称' }, + 'auto.components.new.workspace.SmartWorkspaceNameField.766083a596': { zh: '”' }, + 'auto.components.new.workspace.SmartWorkspaceNameField.9c004911c3': { zh: 'GitLab' }, + 'auto.components.new.workspace.SmartWorkspaceNameField.eadf877af5': { zh: '保留' }, + 'auto.components.crash.report.CrashReportDialog.56a3dfa283': { zh: '无法发送崩溃报告。' }, + 'auto.components.crash.report.CrashReportDialog.765591798d': { zh: '正在检查崩溃报告…' }, + 'auto.components.crash.report.CrashReportDialog.8b8473c544': { zh: '崩溃报告已复制。' }, + 'auto.components.crash.report.CrashReportDialog.8e24fe4f75': { zh: '崩溃报告已发送。' }, + 'auto.components.crash.report.CrashReportDialog.b175e90213': { zh: '没有可用的崩溃报告。' }, + 'auto.components.crash.report.CrashReportDialog.b2e36f53a1': { + zh: '发送崩溃报告失败。诊断工单 {{value0}} 已上传,但未能关联。' + }, + 'auto.components.crash.report.CrashReportDialog.e59f0b9427': { + zh: '随报告发送经过脱敏且有大小限制的日志包。' + }, + 'auto.components.crash.report.submit.notice.copyDetails': { zh: '复制详细信息' }, + + // Additional key-specific corrections found during full-catalog human review. + 'auto.components.sparse.SparseCheckoutPresetSelect.b3a500c623': { zh: '名称' }, + + 'auto.components.Landing.b673e7cf1b': { + zh: 'Git 项目、源代码管理和工作区管理需要 Git。' + } +} diff --git a/config/scripts/locale-zh-manual-app-shell-key-overrides.mjs b/config/scripts/locale-zh-manual-app-shell-key-overrides.mjs new file mode 100644 index 000000000000..54d2625b7c84 --- /dev/null +++ b/config/scripts/locale-zh-manual-app-shell-key-overrides.mjs @@ -0,0 +1,46 @@ +// Human-reviewed Simplified Chinese for application shell, navigation, status, and feature-tip surfaces. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_APP_SHELL_KEY_OVERRIDES = { + // Additional key-specific corrections found during full-catalog human review. + 'auto.components.Terminal.7958465754': { + zh: '本地终端中仍有进程正在运行。仍要关闭窗口吗?' + }, + + 'auto.components.status.bar.WorkspaceSpaceCompactPanel.6a5dc3c61a': { zh: '查看' }, + + 'auto.components.status.bar.UpdateStatusSegment.5cd13105a3': { + zh: '更新失败。点击可展开。' + }, + + 'auto.components.settings.OrchestrationExamplesDialog.3d1aa105e3': { zh: '复制提示词' }, + + 'auto.components.WorktreeJumpPalette.66b5a67bee': { zh: 'Esc' }, + + 'auto.components.cmd.j.quick.actions.verbs.newShell': { zh: '新建 shell' }, + + 'auto.components.feature.tips.FeatureTipsModal.splitWorktreesPrompt': { + zh: '“将此 PR 拆成两个{{term}},并分别为它们创建 PR。”' + }, + + 'auto.components.feature.tips.FeatureTipsModal.handoffReviewPrompt': { + zh: '“当{{term}} X 中的智能体完成后,把评审任务发给它。”' + }, + + 'auto.components.feature.tips.CmdJPaletteTipDialog.8241897205': { + zh: '如需重新绑定快捷键,请前往' + }, + + 'auto.components.orca.profiles.switcher.account': { zh: '账户' }, + + 'auto.components.orca.profiles.switcher.accountTitle': { zh: 'Orca 账户' }, + + 'auto.components.WorktreeJumpPalette.7726ce9970': { zh: '移动端模拟器标签页已不存在' }, + + 'auto.components.settings.OrchestrationExamplesDialog.4e46da1889': { + zh: '复制提示词失败。' + }, + + 'auto.components.tab.bar.tab.create.menu.options.6d8b6b4117': { zh: '新建文件' }, + + 'auto.components.cmd.j.quick.actions.verbs.newFile': { zh: '新建文件' } +} diff --git a/config/scripts/locale-zh-manual-automation-key-overrides.mjs b/config/scripts/locale-zh-manual-automation-key-overrides.mjs new file mode 100644 index 000000000000..dc350e35a2ff --- /dev/null +++ b/config/scripts/locale-zh-manual-automation-key-overrides.mjs @@ -0,0 +1,81 @@ +// Human-reviewed Simplified Chinese for automation scheduling and run history. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_AUTOMATION_KEY_OVERRIDES = { + // Automations. + 'auto.components.automations.AutomationCustomCronPanel.3e3b2c369f': { zh: 'Cron 表达式' }, + 'auto.components.automations.AutomationCustomCronPanel.cadb7b0bc9': { zh: '无效' }, + 'auto.components.automations.AutomationDetail.401f40ae79': { zh: '预计费用' }, + 'auto.components.automations.AutomationDetail.620b22145e': { zh: '补跑宽限期' }, + 'auto.components.automations.AutomationDetail.a1d52c2189': { zh: '用量统计覆盖范围' }, + 'auto.components.automations.AutomationDetail.de0fedac06': { zh: 'new_per_run' }, + 'auto.components.automations.AutomationDetail.eaa02014f8': { zh: '已启用' }, + 'auto.components.automations.AutomationEditorDialog.a4ac8fcc62': { zh: '/goal' }, + 'auto.components.automations.AutomationEditorDialog.ff5db28639': { zh: '现有' }, + 'auto.components.automations.AutomationEditorDialogHeader.0a75e5e2fa': { + zh: '创建 Hermes 自动化' + }, + 'auto.components.automations.AutomationMissedRunGraceField.3d70c185c8': { + zh: '如果 Orca 或执行主机在计划时间不可用,只要在此宽限期内恢复可用,Orca 就会补跑一次。更早错过的运行会被跳过。' + }, + 'auto.components.automations.AutomationMissedRunGraceField.3df53d554a': { + zh: '补跑宽限期说明' + }, + 'auto.components.automations.AutomationMissedRunGraceField.529dc6c0b7': { zh: '无宽限期' }, + 'auto.components.automations.AutomationMissedRunGraceField.fc089e5fde': { zh: '补跑宽限期' }, + 'auto.components.automations.AutomationPrecheckFields.bb2dfb3629': { zh: '超时' }, + 'auto.components.automations.AutomationRunHistory.402651bfb6': { zh: '暂无运行记录。' }, + 'auto.components.automations.AutomationRunHistory.53fc5f07ab': { zh: '运行记录' }, + 'auto.components.automations.AutomationRunHistory.86a248187e': { zh: '费用' }, + 'auto.components.automations.AutomationRunPageFrame.33741dd973': { zh: '返回运行记录' }, + 'auto.components.automations.AutomationSchedulePicker.233b8c94b6': { zh: '频率' }, + 'auto.components.automations.AutomationSchedulePicker.c3e39e17cf': { zh: '自定义' }, + 'auto.components.automations.AutomationSessionField.4bdce31f37': { zh: '会话复用说明' }, + 'auto.components.automations.AutomationSessionField.c90888ee94': { zh: '新会话' }, + 'auto.components.automations.AutomationSessionField.f3c76dce51': { zh: '复用' }, + 'auto.components.automations.AutomationsPage.0ae52dd760': { zh: 'hermes' }, + 'auto.components.automations.AutomationsPage.0e110a3469': { zh: '运行记录' }, + 'auto.components.automations.AutomationsPage.2430fecf53': { + zh: '保存前请选择运行位置并输入提示词。' + }, + 'auto.components.automations.AutomationsPage.25060635c6': { zh: '新增' }, + 'auto.components.automations.AutomationsPage.36f71740a7': { zh: '所选工作区' }, + 'auto.components.automations.AutomationsPage.37288942f0': { zh: '已恢复外部自动化。' }, + 'auto.components.automations.AutomationsPage.4d7878402c': { zh: '外部自动化已加入队列。' }, + 'auto.components.automations.AutomationsPage.77b81bc4ac': { zh: '已创建 Hermes 自动化。' }, + 'auto.components.automations.AutomationsPage.7934ee0d81': { zh: '连接 SSH' }, + 'auto.components.automations.AutomationsPage.9f2855677c': { zh: 'SSH 已连接。' }, + 'auto.components.automations.AutomationsPage.a1bdb57008': { zh: '自动化运行已加入队列。' }, + 'auto.components.automations.AutomationsPage.aecdc3681f': { zh: '可管理' }, + 'auto.components.automations.AutomationsPage.bb1b2cd31e': { zh: '概览' }, + 'auto.components.automations.AutomationsPage.c3a28c9793': { zh: '选择自动化以查看运行记录。' }, + 'auto.components.automations.AutomationsPage.dd0bc7a1ba': { zh: 'new_per_run' }, + 'auto.components.automations.ExternalAutomationManagers.0a2d4359a8': { zh: '可管理' }, + 'auto.components.automations.ExternalAutomationManagers.20fd7a3a15': { zh: '下次' }, + 'auto.components.automations.ExternalAutomationManagers.330b3c32e8': { zh: '可用' }, + 'auto.components.automations.ExternalAutomationManagers.3d58d5b67d': { zh: '无' }, + 'auto.components.automations.ExternalAutomationManagers.5820648765': { zh: '上次' }, + 'auto.components.automations.ExternalAutomationManagers.844f1acb72': { zh: '已找到' }, + 'auto.components.automations.ExternalAutomationManagers.b3feba84c7': { zh: '活跃' }, + 'auto.components.automations.ExternalAutomationManagers.bf5f67b590': { zh: 'hermes' }, + 'auto.components.automations.ExternalAutomationManagers.e02f970595': { + zh: '未找到外部自动化管理器。' + }, + 'auto.components.automations.ExternalAutomationRunTable.0ba9c0a95c': { zh: '下一页' }, + 'auto.components.automations.ExternalAutomationRunTable.2d4388a908': { zh: '运行记录' }, + 'auto.components.automations.ExternalAutomationRunTable.52d468a0b8': { zh: '上一页' }, + 'auto.components.automations.HermesCronOutputView.4557213074': { zh: '响应' }, + 'auto.components.automations.automation.templates.6023075b27': { zh: '每日变更评审' }, + 'auto.components.automations.automation.templates.8a0228bea3': { zh: '每小时检查队列' }, + 'auto.components.automations.automation.templates.releasePrep.prompt': { + zh: '准备一份发布就绪摘要。检查阻塞项、尚未合并的高风险更改、缺失的验证和文档空白,最后给出简短明确的发布或暂缓发布建议。' + }, + 'auto.components.automations.automation.templates.repoHealth.category': { zh: '仓库健康状况' }, + 'auto.components.automations.external.automation.schedule.display.a8e92b815a': { + zh: '执行计划不可用' + }, + + // Additional key-specific corrections found during full-catalog human review. + 'auto.components.automations.automation.templates.recurringReview.name': { + zh: '每日变更评审' + } +} diff --git a/config/scripts/locale-zh-manual-browser-key-overrides.mjs b/config/scripts/locale-zh-manual-browser-key-overrides.mjs new file mode 100644 index 000000000000..5f6fccb8277a --- /dev/null +++ b/config/scripts/locale-zh-manual-browser-key-overrides.mjs @@ -0,0 +1,106 @@ +// Human-reviewed Simplified Chinese for browser profiles, cookies, and Computer Use settings. +// Why: profile and permission terminology needs product context that broad MT repair cannot infer. +export const ZH_MANUAL_BROWSER_KEY_OVERRIDES = { + 'auto.components.settings.BrowserPane.81ff774667': { zh: '取消' }, + 'auto.components.settings.BrowserPane.7d4c0a2aa4': { zh: '配置文件名称' }, + 'auto.components.settings.BrowserPane.612f7f6861': { zh: '创建配置文件失败。' }, + 'auto.components.settings.BrowserPane.8f22b7580d': { zh: '已创建配置文件“{{value0}}”。' }, + 'auto.components.settings.BrowserPane.8481ee0331': { zh: '新建浏览器配置文件' }, + 'auto.components.settings.BrowserPane.c0f85056d9': { + zh: '此 Orca 服务器上的浏览器配置文件。' + }, + 'auto.components.settings.BrowserPane.86b7c83fee': { zh: '本机' }, + 'auto.components.settings.BrowserPane.6480776a03': { zh: '所选主机上的浏览器配置文件。' }, + 'auto.components.settings.BrowserPane.5e19a692f7': { zh: '主机' }, + 'auto.components.settings.BrowserPane.6f2584b39e': { zh: '添加配置文件' }, + 'auto.components.settings.BrowserPane.e4aaf8051b': { + zh: '工具栏菜单导入 Cookie,并为各标签页切换配置文件。' + }, + 'auto.components.settings.BrowserPane.cd47bc9622': { + zh: '为新建浏览器标签页选择默认配置文件。可通过' + }, + 'auto.components.settings.BrowserPane.2d66a6efb5': { zh: '会话与 Cookie' }, + 'auto.components.settings.BrowserPane.aa1074bfe9': { + zh: '管理浏览器配置文件,并从 Chrome、Edge、Comet 或其他浏览器导入 Cookie。' + }, + 'auto.components.settings.BrowserPane.113cd2dc9b': { zh: '会话与 Cookie' }, + 'auto.components.settings.BrowserPane.d3eb69c0aa': { zh: '链接路由' }, + 'auto.components.settings.BrowserPane.3e46903ad4': { + zh: '用于在地址栏中输入非 URL 文本的情况。' + }, + 'auto.components.settings.BrowserPane.0d9c987f21': { zh: '默认搜索引擎' }, + 'auto.components.settings.BrowserPane.7b225c78f5': { + zh: '在地址栏中输入非 URL 文本时使用的搜索引擎。' + }, + 'auto.components.settings.BrowserPane.64898ecdab': { zh: '创建' }, + 'auto.components.settings.BrowserPane.7b649a578a': { zh: '正在创建…' }, + 'auto.components.settings.BrowserPane.4399c77caa': { zh: '默认' }, + 'auto.components.settings.BrowserProfileRow.8e636cae25': { + zh: '已移除配置文件“{{value0}}”。' + }, + 'auto.components.settings.BrowserProfileRow.2d4bea7f35': { zh: '已清除默认 Cookie。' }, + 'auto.components.settings.BrowserProfileRow.ebb78dfd6f': { zh: '从文件导入…' }, + 'auto.components.settings.BrowserProfileRow.7df818977e': { zh: '来源' }, + 'auto.components.settings.BrowserProfileRow.cdec84552f': { zh: '导入 Cookie' }, + 'auto.components.settings.BrowserProfileRow.796d846483': { zh: '尚未导入 Cookie' }, + 'auto.components.settings.BrowserProfileRow.c29648fe5b': { zh: '当前使用' }, + 'auto.components.settings.BrowserProfileRow.d420c43729': { + zh: '已将来自 {{value1}}{{value2}} 的 {{value0}} 个 Cookie 导入 {{value3}}。' + }, + 'auto.components.settings.BrowserProfileRow.b4c167764d': { + zh: '已将文件中的 {{value0}} 个 Cookie 导入 {{value1}}。' + }, + 'auto.components.settings.BrowserProfileRow.a3f8c2d1e0b4': { + zh: '已将来自 {{value1}}({{value2}})的 {{value0}} 个 Cookie 导入 {{value3}}。' + }, + 'auto.components.settings.BrowserProfileRow.b4e9d3f2a1c5': { + zh: '已将来自 {{value1}} 的 {{value0}} 个 Cookie 导入 {{value2}}。' + }, + 'auto.components.settings.BrowserProfileRow.c5a273a809': { zh: '来自 {{value0}}' }, + 'auto.components.settings.BrowserUseComputerUseNotice.15b5e680ba': { + zh: '打开 Computer Use' + }, + 'auto.components.settings.BrowserUseComputerUseNotice.79209b37b9': { + zh: '如果不适合导入 Cookie,Computer Use 可以控制本地应用,并在适用时使用浏览器中已有的登录会话。请安装 Computer Use 技能;在 macOS 上还需授予隐私权限。' + }, + 'auto.components.settings.BrowserUseComputerUseNotice.333984cf90': { + zh: '使用现有浏览器会话' + }, + 'auto.components.settings.ComputerUsePane.1735461723': { + zh: '允许智能体检查并操作本地桌面应用。' + }, + 'auto.components.settings.ComputerUsePane.93255aaf18': { zh: 'Computer Use 技能' }, + 'auto.components.settings.ComputerUsePane.45f8e22c2e': { zh: '打开' }, + 'auto.components.settings.ComputerUsePane.d95d1cfab8': { zh: '刷新' }, + 'auto.components.settings.ComputerUsePane.0c29da5805': { zh: '就绪' }, + 'auto.components.settings.ComputerUsePane.3383ea1aab': { + zh: '无法重置 Computer Use 权限' + }, + 'auto.components.settings.ComputerUsePane.f189f448a3': { zh: '重置 Computer Use 访问权限' }, + 'auto.components.settings.ComputerUsePane.5c45349665': { + zh: '无法打开 Computer Use 权限设置' + }, + 'auto.components.settings.ComputerUsePane.7801ac08ec': { + zh: 'Computer Use 权限仅在 macOS 上需要' + }, + 'auto.components.settings.ComputerUsePane.740766c291': { + zh: 'Computer Use 已设置完成' + }, + 'auto.components.settings.ComputerUsePane.697005758f': { + zh: '已打开 macOS“隐私与安全性”' + }, + 'auto.components.settings.ComputerUsePane.2168fa5ab0': { + zh: '无法加载 Computer Use 权限' + }, + 'auto.components.settings.ComputerUsePane.0c9a33f468': { + zh: '捕获应用窗口,供智能体检查画面状态。' + }, + 'auto.components.settings.ComputerUsePane.07bbe4c4cb': { zh: '截图' }, + 'auto.components.settings.ComputerUsePane.4d03dec2d0': { + zh: '读取应用界面树并执行请求的操作。' + }, + 'auto.components.settings.ComputerUsePane.6b5a2cd3a5': { zh: '辅助功能' }, + 'auto.components.settings.ComputerUsePane.6b17602073': { zh: '重置访问权限' }, + 'auto.components.settings.ComputerUsePane.506f2acf7a': { zh: '正在重置访问权限…' }, + 'auto.components.settings.ComputerUsePane.4b65070096': { zh: 'darwin' } +} diff --git a/config/scripts/locale-zh-manual-collaboration-key-overrides.mjs b/config/scripts/locale-zh-manual-collaboration-key-overrides.mjs new file mode 100644 index 000000000000..5423eb2e9f6a --- /dev/null +++ b/config/scripts/locale-zh-manual-collaboration-key-overrides.mjs @@ -0,0 +1,257 @@ +// Human-reviewed Simplified Chinese for review, editor, and automation surfaces. +// Why: these strings need full UI context; value-wide substitutions would corrupt unrelated copy. +export const ZH_MANUAL_COLLABORATION_KEY_OVERRIDES = { + 'auto.components.GitHubItemDialog.0ab4664a8b': { zh: '从议题创建工作区' }, + 'auto.components.GitHubItemDialog.c0253318d6': { zh: '无法同步此拉取请求的已查看状态。' }, + 'auto.components.GitHubItemDialog.90020cc1f3': { zh: '此拉取请求尚无检查结果。' }, + 'auto.components.GitHubItemDialog.e7007aa1d8': { zh: '缺少仓库路径,无法刷新检查。' }, + 'auto.components.GitHubItemDialog.a27ee5ca1a': { zh: '这会更新 GitHub 上的拉取请求。' }, + 'auto.components.GitHubItemDialog.de45fedf7b': { zh: '这会关闭 GitHub 上的拉取请求。' }, + 'auto.components.GitHubItemDialog.745c9089ec': { zh: '缺少仓库路径,无法回复。' }, + 'auto.components.GitHubItemDialog.d1fa2cf888': { zh: '缺少 PR head SHA,无法发表评论。' }, + 'auto.components.GitHubItemDialog.86f809e2ce': { zh: '在此评审线程中回复' }, + 'auto.components.GitHubItemDialog.b0b09778c8': { zh: '添加评审评论失败。' }, + 'auto.components.GitHubItemDialog.b4af16bf43': { zh: '此拉取请求没有可用的仓库上下文。' }, + 'auto.components.GitHubItemDialog.timeline.mentioned': { zh: '提到了此项' }, + 'auto.components.GitHubItemDialog.timeline.closed': { zh: '关闭了此项' }, + 'auto.components.GitHubItemDialog.timeline.reopened': { zh: '重新打开了此项' }, + 'auto.components.GitHubItemDialog.timeline.moved': { zh: '移动了此项' }, + + 'auto.components.PullRequestPage.filesUnavailable': { zh: '无法加载变更文件。' }, + 'auto.components.PullRequestPage.996a1897d2': { zh: '无法同步此拉取请求的已查看状态。' }, + 'auto.components.PullRequestPage.3912daf310': { zh: '此拉取请求尚无检查结果。' }, + 'auto.components.PullRequestPage.c057f2fcb0': { zh: '缺少仓库路径,无法刷新检查。' }, + 'auto.components.PullRequestPage.a63b3c159c': { zh: '这会更新 GitHub 上的拉取请求。' }, + 'auto.components.PullRequestPage.5a65651096': { zh: '这会关闭 GitHub 上的拉取请求。' }, + 'auto.components.PullRequestPage.6885c619e7': { zh: '缺少仓库路径,无法回复。' }, + 'auto.components.PullRequestPage.d8c3ba91c4': { zh: '缺少 PR head SHA,无法发表评论。' }, + 'auto.components.PullRequestPage.408e634fbb': { zh: '在此评审线程中回复' }, + 'auto.components.PullRequestPage.19628e058d': { zh: '添加评审评论失败。' }, + 'auto.components.PullRequestPage.1ae11c905c': { zh: '此拉取请求没有可用的仓库上下文。' }, + 'auto.components.PullRequestPage.61452f2143': { zh: '从议题创建工作区' }, + 'auto.components.PullRequestPage.25690a3855': { zh: '从 PR 创建工作区' }, + 'auto.components.PullRequestPage.61bfc81ada': { zh: '无法打开与此拉取请求关联的工作区。' }, + + 'auto.components.TaskPage.d0e3c8f933': { zh: '没有匹配的 GitHub 工作项' }, + 'auto.components.TaskPage.59c14d34a2': { zh: '在以下页面创建 token:' }, + 'auto.components.TaskPage.606a85c774': { zh: '未关闭' }, + 'auto.components.TaskPage.995dd6af9b': { zh: '查看 PR 检查' }, + 'auto.components.TaskPage.4b6e40e42c': { zh: '全部未关闭' }, + 'auto.components.TaskPage.ff53631e6f': { zh: '刷新 GitHub 工作项' }, + 'auto.components.TaskPage.6ffa6be99f': { zh: '正在刷新 GitHub 工作项' }, + 'auto.components.TaskPage.31f81cc334': { zh: '正在刷新 GitHub 工作项…' }, + 'auto.components.TaskPage.f294c500ef': { zh: '没有符合此筛选条件的 GitLab 工作项。' }, + 'auto.components.TaskPage.e104fa3d3d': { zh: '从议题创建工作区' }, + 'auto.components.TaskPage.e4b29c5bcf': { zh: '从 PR 创建工作区' }, + 'auto.components.TaskPage.534a9c6017': { zh: '无法打开与此拉取请求关联的工作区。' }, + 'auto.components.TaskPage.linearEmptyUnfilteredScope': { + zh: '此工作区范围内没有议题。请尝试搜索或调整团队。' + }, + + 'auto.components.GitLabItemDialog.30c97083c2': { zh: 'GitLab 工作项详情' }, + 'auto.components.GitLabItemDialog.2f9b27f838': { zh: '作业日志' }, + 'auto.components.GitLabItemDialog.3a051b8ade': { zh: '工作项' }, + 'auto.components.QuickOpen.61b1c871a6': { zh: '打开' }, + 'auto.components.LinearIssueWorkspace.30a7f56c0a': { zh: '从议题创建工作区' }, + 'auto.components.LinearItemDrawer.04008e6c46': { zh: '从议题创建工作区' }, + 'auto.components.github.project.ProjectViewWrapper.7037c8f5f1': { zh: '仓库未添加到 Orca' }, + 'auto.components.github.PRFilterSections.d78b60b5c2': { zh: '未关闭' }, + 'auto.components.github.pr.merge.state.a80132573b': { + zh: 'GitHub 仍在计算此拉取请求的合并状态' + }, + 'auto.components.github.pr.merge.state.1432ecff30': { + zh: 'GitHub 显示此 PR 可以合并,但部分检查失败' + }, + 'auto.components.github.pr.merge.state.039c072f94': { zh: '落后于基础分支' }, + 'auto.components.github.pr.merge.state.b289646bcd': { + zh: 'GitHub 显示此拉取请求已被要求更改' + }, + 'auto.components.github.pr.merge.state.c606463dc2': { zh: '已请求更改' }, + 'auto.components.github.pr.merge.state.a20db875ed': { + zh: '此拉取请求需要通过评审才能合并' + }, + 'auto.components.github.pr.merge.state.1f8eb81c0e': { zh: '需要评审批准' }, + 'auto.components.github.pr.merge.state.f03028e055': { zh: '此拉取请求仍是草稿' }, + 'auto.components.github.pr.merge.state.820fd21663': { zh: '此拉取请求已关闭' }, + 'auto.components.github.pr.merge.state.62eb8d39da': { zh: '此拉取请求已合并' }, + 'auto.components.github.pr.merge.state.331ebe1170': { + zh: '将此拉取请求加入 GitHub 合并队列' + }, + 'auto.components.github.pr.merge.state.62703b1dc4': { + zh: '此拉取请求已启用 GitHub 自动合并' + }, + 'auto.components.github.pr.merge.state.8f6cb3772f': { + zh: '满足要求后自动合并此拉取请求' + }, + + 'auto.components.skills.SkillsPage.aa59462502': { zh: '仓库' }, + 'auto.components.skills.SkillsPage.ab5b777350': { + zh: '已检查本地主目录、仓库、内置技能和插件技能文件夹。' + }, + 'auto.components.stats.StatsPane.1c96f433e2': { zh: '智能体工作时长' }, + + 'auto.components.right.sidebar.useFileDeletion.77fdc36183': { + zh: '删除 {{count}} 个项目?' + }, + 'auto.components.right.sidebar.AiVaultPanelControls.hostScopeAriaLabel': { + zh: '会话历史主机:{{value0}}' + }, + 'auto.components.right.sidebar.aiVaultSessionLogOpen.workspaceGone': { + zh: '无法打开日志:工作区已不可用。' + }, + 'auto.components.right.sidebar.aiVaultSessionLogOpen.notAuthorized': { + zh: '无法打开日志:路径未获授权。' + }, + 'auto.components.right.sidebar.aiVaultSessionLogOpen.alreadyEditable': { + zh: '日志已在编辑中。' + }, + 'auto.components.right.sidebar.ChecksPanel.5788d1059d': { + zh: '无法更新评审线程。请检查 GitHub API 配额。' + }, + 'auto.components.right.sidebar.ChecksPanel.d00ebdc402': { + zh: '使用 AI 解决 {{value0}} 条评审评论' + }, + 'auto.components.right.sidebar.ChecksPanel.495b2f8c4b': { + zh: '智能体已启动,但无法将所选评审评论标记为已解决。' + }, + 'auto.components.right.sidebar.ChecksPanel.3c3ad3a1d2': { + zh: '智能体已启动。所选评审评论均无法在托管平台上标记为已解决。' + }, + 'auto.components.right.sidebar.CreatePullRequestDialog.b504b3ceb1': { + zh: '创建托管评审前的详细信息。' + }, + 'auto.components.right.sidebar.CreatePullRequestDialog.f658ff2455': { + zh: '创建托管评审前,请确认目标分支和 {{value0}} 详情。' + }, + 'auto.components.right.sidebar.CreatePullRequestDialog.edc35a7027': { + zh: '{{value0}} #{{value1}} 已处于打开状态' + }, + 'auto.components.right.sidebar.FileExplorerRow.1a3df04ae1': { zh: '打开' }, + 'auto.components.right.sidebar.SourceControl.ac8cbe3bf5': { + zh: '将鼠标悬停在差异视图的某一行上,然后点击 + 添加笔记。' + }, + 'auto.components.right.sidebar.SourceControl.37a81f29ad': { + zh: '正在生成提交信息。点击可停止。' + }, + 'auto.components.right.sidebar.SourceControl.1a6a6e0bc5': { zh: '生成托管评审详情' }, + 'auto.components.right.sidebar.SourceControl.c9ad22888e': { + zh: '选择此仓库的分支比较目标。' + }, + 'auto.components.right.sidebar.SourceControl.e131cd7128': { + zh: '源代码管理仅适用于 Git 仓库' + }, + 'auto.components.right.sidebar.SourceControl.hugeRepoIgnorePrompt': { + zh: '此仓库的活动变更过多。是否将“{{value0}}”添加到 .gitignore?' + }, + 'auto.components.right.sidebar.SourceControl.f394c6128a': { + zh: '没有可用于解释此提交的智能体' + }, + 'auto.components.right.sidebar.SourceControl.eef5446523': { + zh: '{{value0}} #{{value1}} 已处于打开状态' + }, + 'auto.components.right.sidebar.SourceControl.createPrIntentGeneratingDetails': { + zh: '正在生成评审详情…' + }, + 'auto.components.right.sidebar.SourceControl.createPrIntentBranchChangedDuringDetails': { + zh: '生成评审详情时分支发生了变化。请重试创建 PR。' + }, + 'auto.components.right.sidebar.SourceControl.createPrIntentCreatingReview': { + zh: '正在创建评审…' + }, + 'auto.components.right.sidebar.SourceControl.4b4a7de138': { zh: '在浏览器中打开评审页面' }, + 'auto.components.right.sidebar.SourceControlAgentActionDialog.808cfe0a3b': { + zh: '此仓库' + }, + 'auto.components.right.sidebar.SourceControlTextGenerationDialog.7f1ec309a4': { + zh: '保存为所有仓库的默认值' + }, + 'auto.components.right.sidebar.SourceControlTextGenerationDialog.5959da1e4d': { + zh: '仅保存到此仓库' + }, + 'auto.components.right.sidebar.checks.panel.content.3fff651d32': { zh: '添加 PR 评论' }, + 'auto.components.right.sidebar.checks.panel.content.74c6885b8a': { zh: '更多评论操作' }, + 'auto.components.right.sidebar.checks.panel.content.2dd5ddabc4': { zh: '工作流 #' }, + 'auto.components.right.sidebar.checks.panel.content.066fedd446': { zh: '失败的作业' }, + 'auto.components.right.sidebar.checks.panel.content.d91f2a6c39': { + zh: '将 {{value0}} 条排队中的评论发送给 AI' + }, + 'auto.components.right.sidebar.checks.panel.content.49ea0937e4': { + zh: '将评论加入待解决列表' + }, + 'auto.components.right.sidebar.checks.panel.content.c3a8e5d710': { + zh: '待评审 · {{value0}}' + }, + 'auto.components.right.sidebar.checks.panel.empty.state.b597440265': { + zh: '刷新此分支的 GitHub 状态,以加载检查和评审信息。' + }, + + 'auto.components.editor.CombinedDiffViewer.fd8892b120': { zh: '没有可显示的更改' }, + 'auto.components.editor.EditorPanelHeader.146cb5473c': { + zh: '目录可在富文本或预览模式下使用' + }, + 'auto.components.editor.EditorPanelHeader.f0fd4174b5': { + zh: '打开文件标签页以使用 Markdown 富文本编辑' + }, + 'auto.components.editor.EditorViewToggle.aff15f94f5': { zh: '富文本编辑器' }, + 'auto.components.editor.MarkdownPreview.0f9969a159': { zh: '跳转到第一条评审笔记' }, + 'auto.components.editor.RichMarkdownAnnotationOverlay.6f2f3a6001': { zh: '添加评审笔记' }, + 'auto.components.editor.RichMarkdownErrorBoundary.4a5de9f2f0': { + zh: '切换到源码模式,或点击“重试”重新加载富文本视图。' + }, + 'auto.components.editor.RichMarkdownErrorBoundary.dfdf1cacd4': { + zh: 'Markdown 富文本编辑器遇到意外错误,已重置以确保 Orca 的其余部分正常响应。' + }, + 'auto.components.editor.RichMarkdownReviewRailActions.8aaf2c4c69': { zh: '显示评审笔记' }, + 'auto.components.editor.RichMarkdownReviewRailActions.af02dc2456': { zh: '隐藏评审笔记' }, + 'auto.components.diff.comments.DiffCommentCard.6978871a3d': { zh: '打开' }, + + 'auto.components.automations.AutomationDetail.221916d93c': { + zh: '创建自动化,开始定时执行智能体任务。' + }, + 'auto.components.automations.AutomationEditorDialog.6f9610e667': { + zh: '“工作树”会在所选工作区中运行;“每次新建工作区”则会基于所选分支创建全新的工作区。' + }, + 'auto.components.automations.AutomationEditorDialog.2c3fd9bfa1': { zh: '工作区模式说明' }, + 'auto.components.automations.AutomationsPage.8705757e27': { zh: '任务' }, + 'auto.components.automations.automation.templates.37571fcb16': { + zh: '查找卡住的任务、过期的生成文件和失败的本地验证。' + }, + 'auto.components.automations.automation.templates.3b7281c75f': { + zh: '评审近期变更,并指出正确性、用户体验和测试覆盖率方面的风险。' + }, + 'auto.components.automations.automation.templates.a7fbd32ddb': { + zh: '每个工作日检查依赖项、失败的测试和风险较高的待处理变更。' + }, + 'auto.components.automations.automation.templates.repoHealth.prompt': { + zh: '检查仓库健康状况,包括依赖项更新、失败的测试、lint/typecheck 状态和风险较高的待处理变更。总结发现并建议下一步操作。' + }, + 'auto.components.automations.automation.templates.recurringReview.prompt': { + zh: '评审此工作区的近期变更,重点关注正确性风险、用户体验回归、缺失的测试和后续任务。报告应简短,并明确可执行事项。' + }, + 'auto.components.automations.automation.templates.maintenance.prompt': { + zh: '检查卡住的任务、过期的生成文件、失败的验证以及其他需要人工处理的事项。仅报告需处理的问题。' + }, + 'auto.components.automations.AutomationSetupDecisionField.874b72195b': { + zh: '自动化创建工作区时,按手动创建工作树的相同方式完成准备:运行项目设置并打开终端标签页。' + }, + + 'auto.components.crash.report.submit.notice.unknownError': { + zh: '崩溃报告请求在返回原因前失败。' + }, + 'auto.components.crash.report.submit.notice.ticketUploaded': { + zh: '诊断工单 {{value0}} 已上传,但未能关联。' + }, + 'auto.components.crash.report.submit.notice.uncheckDiagnostics': { + zh: '取消勾选“附加最近的诊断日志”后重试,或复制详细信息。' + }, + 'auto.components.crash.report.submit.notice.checkConnection': { + zh: '检查网络连接后重试,或复制详细信息。' + }, + 'auto.components.crash.report.submit.notice.notSent': { zh: '崩溃报告未发送' }, + 'auto.components.crash.report.submit.notice.sentWithoutDiagnostics': { + zh: '崩溃报告已发送,但未附加诊断日志' + }, + 'auto.components.crash.report.submit.notice.diagnosticsReason': { + zh: '未附加诊断日志:{{value0}}' + } +} diff --git a/config/scripts/locale-zh-manual-editor-key-overrides.mjs b/config/scripts/locale-zh-manual-editor-key-overrides.mjs new file mode 100644 index 000000000000..21aaeb0eba19 --- /dev/null +++ b/config/scripts/locale-zh-manual-editor-key-overrides.mjs @@ -0,0 +1,112 @@ +// Human-reviewed Simplified Chinese for editors, diffs, notebooks, and rich Markdown. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_EDITOR_KEY_OVERRIDES = { + // Editors, diffs, notebooks, and rich Markdown. + 'auto.components.editor.CheckRunDetailsPanel.066fedd446': { zh: '失败的作业' }, + 'auto.components.editor.CheckRunDetailsPanel.49731703ea': { zh: '作业' }, + 'auto.components.editor.CheckRunDetailsPanel.fd46a70f1a': { zh: '已启动' }, + 'auto.components.editor.CheckRunJobs.3e6c9a2b71': { zh: '待处理' }, + 'auto.components.editor.CombinedDiffFileTree.cd0e0ed79e': { zh: '筛选差异文件' }, + 'auto.components.editor.CombinedDiffViewer.948a5fd6c8': { zh: '清除笔记' }, + 'auto.components.editor.CombinedDiffViewer.982d14bfa5': { zh: '打开所有更改' }, + 'auto.components.editor.CombinedDiffViewer.e3b9a6ce02': { zh: '更多' }, + 'auto.components.editor.CombinedDiffViewer.eb5f40e49c': { + zh: '此差异视图会排除未解决的冲突,因为常规双向差异流程无法安全处理冲突。' + }, + 'auto.components.editor.CombinedDiffViewer.f786fd54e1': { zh: '行内' }, + 'auto.components.editor.EditorContent.9640d1d3db': { + zh: '正在预览差异中的修改后版本。切换到源码模式可查看具体更改。' + }, + 'auto.components.editor.EditorPanelHeader.94756f08ba': { zh: '切换到行内差异' }, + 'auto.components.editor.EditorPanelHeader.c98ce191da': { zh: '此差异没有可打开的修改后文件' }, + 'auto.components.editor.EditorPanelHeader.fb8331694e': { zh: '在侧边打开预览' }, + 'auto.components.editor.EditorPanelMarkdownActionsMenu.10c39d58c1': { zh: '隐藏前置元数据' }, + 'auto.components.editor.EditorPanelMarkdownActionsMenu.8c8b7f5ff5': { zh: '显示前置元数据' }, + 'auto.components.editor.EditorViewToggle.e408aa9cd5': { zh: '表格' }, + 'auto.components.editor.IpynbViewer.07e7d96612': { zh: '单元格' }, + 'auto.components.editor.IpynbViewer.10ed04a685': { + zh: '笔记本单元格会从笔记本所在文件夹在本机执行 Python。仅运行来自可信文件的单元格。' + }, + 'auto.components.editor.IpynbViewer.329764e9fc': { zh: '测试版' }, + 'auto.components.editor.IpynbViewer.8c3b21369a': { zh: 'nbformat' }, + 'auto.components.editor.MarkdownPreview.322afab6ff': { zh: '评审笔记' }, + 'auto.components.editor.MarkdownPreview.94b520a96a': { zh: '已复制笔记' }, + 'auto.components.editor.MarkdownPreview.bb629de58a': { zh: '复制笔记给智能体' }, + 'auto.components.editor.MarkdownPreview.d737791433': { zh: '为 AI 添加笔记' }, + 'auto.components.editor.MarkdownPreview.f961e94057': { zh: '复制笔记给智能体' }, + 'auto.components.editor.MonacoEditor.68cb83f4a7': { zh: '在所选文本上添加笔记' }, + 'auto.components.editor.NotesSendMenu.44dc5e60a6': { zh: '发送笔记' }, + 'auto.components.editor.ReviewNotesSendMenuContent.03378aea75': { zh: '将笔记发送到' }, + 'auto.components.editor.ReviewNotesSendMenuContent.50f7e753ea': { + zh: '正在将笔记发送给当前智能体…' + }, + 'auto.components.editor.ReviewNotesSendMenuContent.bb9c69a0c9': { + zh: '笔记已发送给当前智能体。' + }, + 'auto.components.editor.ReviewNotesSendMenuContent.f5096c6e4e': { + zh: '无法将笔记发送给当前智能体。' + }, + 'auto.components.editor.RichMarkdownReviewNoteLayer.117432e2c6': { zh: '已复制笔记' }, + 'auto.components.editor.RichMarkdownReviewNoteLayer.3ababd949d': { zh: '评审笔记' }, + 'auto.components.editor.RichMarkdownReviewNoteLayer.9cde7ad994': { zh: '复制笔记给智能体' }, + 'auto.components.editor.RichMarkdownReviewRailActions.636394af72': { zh: '复制笔记给智能体' }, + 'auto.components.editor.RichMarkdownReviewRailActions.a807596997': { zh: '已复制笔记' }, + 'auto.components.editor.RichMarkdownSlashMenu.e2e12b0e98': { zh: '组件' }, + 'auto.components.editor.RichMarkdownToolbar.6d52624712': { zh: '链接' }, + 'auto.components.editor.rich.markdown.slash.commands.0ed9a7b38c': { + zh: '插入 Mermaid 围栏代码块。' + }, + 'auto.components.editor.rich.markdown.slash.commands.19ea597868': { zh: '表格' }, + 'auto.components.editor.rich.markdown.slash.commands.2bf5544faf': { zh: '行内公式' }, + 'auto.components.editor.rich.markdown.slash.commands.2f9d6b4e10': { zh: '设为三级标题' }, + 'auto.components.editor.rich.markdown.slash.commands.41482b15ce': { zh: '设为一级标题' }, + 'auto.components.editor.rich.markdown.slash.commands.565907cf7a': { + zh: '插入行内 LaTeX 公式。' + }, + 'auto.components.editor.rich.markdown.slash.commands.5e0b9c2a71': { zh: '设为四级标题' }, + 'auto.components.editor.rich.markdown.slash.commands.6993a38ad1': { zh: '块级公式' }, + 'auto.components.editor.rich.markdown.slash.commands.7a2c1f9b04': { zh: '设为二级标题' }, + 'auto.components.editor.rich.markdown.slash.commands.89e327e054': { zh: '插入围栏代码块。' }, + 'auto.components.editor.rich.markdown.slash.commands.9a7fe896dc': { zh: '插入普通段落。' }, + 'auto.components.editor.rich.markdown.slash.commands.ae7d0f3f37': { + zh: '插入块级 LaTeX 公式。' + }, + 'auto.components.editor.rich.markdown.slash.commands.ae8377cf6b': { zh: '分隔线' }, + 'auto.components.editor.rich.markdown.slash.commands.e516d3f6e3': { zh: 'Mermaid 图表' }, + 'auto.components.editor.rich.markdown.slash.commands.f82c78a2ee': { zh: '设为正文' }, + 'auto.components.diff.comments.DiffCommentCard.cce596969e': { zh: '删除笔记' }, + 'auto.components.diff.comments.useDiffCommentDecorator.995fa28b50': { zh: '此笔记' }, + + // Additional key-specific corrections found during full-catalog human review. + 'auto.components.editor.IpynbViewer.fd8ac707bc': { zh: '上移单元格' }, + + 'auto.components.editor.UntitledFileRenameDialog.b6ed807cc6': { zh: '名称' }, + + 'auto.components.editor.EditorViewToggle.4d6ccb7ba6': { zh: '源代码' }, + + 'auto.components.editor.EditorContent.e4b074749d': { zh: '文档元数据' }, + + 'auto.components.editor.MarkdownPreview.2b2b31382c': { zh: '文档元数据' }, + + 'auto.components.editor.CheckRunDetailsPanel.5a1c8e3d67': { zh: '中立' }, + + 'auto.components.editor.PdfFind.db56fcd6d2': { zh: '{{value0}} / {{value1}}' }, + + 'auto.components.editor.ImageDiffViewer.a651be62b0': { zh: '修改后' }, + + 'auto.components.editor.ImageDiffViewer.57aac3979a': { zh: '原图' }, + + 'auto.components.editor.ExternalFileChangeBanner.7c41e90d12': { + zh: '文件已在磁盘上被外部修改。若保存当前编辑,将覆盖磁盘中的较新内容。' + }, + + 'auto.components.editor.CombinedDiffViewer.35cc27aeb2': { zh: '在源代码管理中' }, + + 'auto.components.editor.ConflictComponents.28e7db4a90': { zh: '源代码管理' }, + + 'auto.components.editor.CheckRunDetailsPanel.cdbfda4dec': { zh: '注解' }, + + 'auto.components.editor.CombinedDiffViewer.39f8007549': { zh: '查看冲突' }, + + 'auto.components.editor.PdfViewer.3e98d500d2': { zh: 'PDF 预览' } +} diff --git a/config/scripts/locale-zh-manual-interpolated-copy-key-overrides.mjs b/config/scripts/locale-zh-manual-interpolated-copy-key-overrides.mjs new file mode 100644 index 000000000000..34c109b171d8 --- /dev/null +++ b/config/scripts/locale-zh-manual-interpolated-copy-key-overrides.mjs @@ -0,0 +1,388 @@ +// Human-reviewed Simplified Chinese for complete sentences that replace English fragment interpolation. +// Why: CJK grammar cannot safely reuse English verbs, nouns, or plural suffixes as placeholders. +export const ZH_MANUAL_INTERPOLATED_COPY_KEY_OVERRIDES = { + // Pull request and GitHub item actions. + 'auto.components.PullRequestPage.markFileAsViewed': { + zh: '将 {{filePath}} 标记为已查看' + }, + 'auto.components.PullRequestPage.unmarkFileAsViewed': { + zh: '将 {{filePath}} 标记为未查看' + }, + 'auto.components.PullRequestPage.oneReactionAriaLabel': { + zh: '{{reactionEmoji}} 表情回应,共 {{count}} 次' + }, + 'auto.components.PullRequestPage.manyReactionsAriaLabel': { + zh: '{{reactionEmoji}} 表情回应,共 {{count}} 次' + }, + 'auto.components.PullRequestPage.confirmClosePullRequestTitle': { + zh: '关闭拉取请求 #{{number}}?' + }, + 'auto.components.PullRequestPage.closePullRequestConfirmLabel': { zh: '关闭' }, + 'auto.components.PullRequestPage.failedToClosePullRequest': { zh: '关闭拉取请求失败' }, + 'auto.components.PullRequestPage.confirmReopenPullRequestTitle': { + zh: '重新打开拉取请求 #{{number}}?' + }, + 'auto.components.PullRequestPage.reopenPullRequestConfirmLabel': { zh: '重新打开' }, + 'auto.components.PullRequestPage.failedToReopenPullRequest': { + zh: '重新打开拉取请求失败' + }, + 'auto.components.PullRequestPage.squashAndMergeLabel': { zh: '压缩并合并' }, + 'auto.components.PullRequestPage.createMergeCommitLabel': { zh: '创建合并提交' }, + 'auto.components.PullRequestPage.rebaseAndMergeLabel': { zh: '变基并合并' }, + 'auto.components.PullRequestPage.confirmSquashAndMergeTitle': { + zh: '压缩并合并拉取请求 #{{number}}?' + }, + 'auto.components.PullRequestPage.confirmCreateMergeCommitTitle': { + zh: '为拉取请求 #{{number}} 创建合并提交?' + }, + 'auto.components.PullRequestPage.confirmRebaseAndMergeTitle': { + zh: '变基并合并拉取请求 #{{number}}?' + }, + 'auto.components.GitHubItemDialog.markFileAsViewed': { + zh: '将 {{filePath}} 标记为已查看' + }, + 'auto.components.GitHubItemDialog.unmarkFileAsViewed': { + zh: '将 {{filePath}} 标记为未查看' + }, + 'auto.components.GitHubItemDialog.oneReactionAriaLabel': { + zh: '{{reactionEmoji}} 表情回应,共 {{count}} 次' + }, + 'auto.components.GitHubItemDialog.manyReactionsAriaLabel': { + zh: '{{reactionEmoji}} 表情回应,共 {{count}} 次' + }, + 'auto.components.GitHubItemDialog.confirmClosePullRequestTitle': { + zh: '关闭拉取请求 #{{number}}?' + }, + 'auto.components.GitHubItemDialog.closePullRequestConfirmLabel': { zh: '关闭' }, + 'auto.components.GitHubItemDialog.failedToClosePullRequest': { zh: '关闭拉取请求失败' }, + 'auto.components.GitHubItemDialog.confirmReopenPullRequestTitle': { + zh: '重新打开拉取请求 #{{number}}?' + }, + 'auto.components.GitHubItemDialog.reopenPullRequestConfirmLabel': { zh: '重新打开' }, + 'auto.components.GitHubItemDialog.failedToReopenPullRequest': { + zh: '重新打开拉取请求失败' + }, + 'auto.components.GitHubItemDialog.squashAndMergeLabel': { zh: '压缩并合并' }, + 'auto.components.GitHubItemDialog.createMergeCommitLabel': { zh: '创建合并提交' }, + 'auto.components.GitHubItemDialog.rebaseAndMergeLabel': { zh: '变基并合并' }, + 'auto.components.GitHubItemDialog.confirmSquashAndMergeTitle': { + zh: '压缩并合并拉取请求 #{{number}}?' + }, + 'auto.components.GitHubItemDialog.confirmCreateMergeCommitTitle': { + zh: '为拉取请求 #{{number}} 创建合并提交?' + }, + 'auto.components.GitHubItemDialog.confirmRebaseAndMergeTitle': { + zh: '变基并合并拉取请求 #{{number}}?' + }, + + // Task counts and source-control operations. + 'auto.components.TaskPage.oneSelectedMember': { zh: '{{count}} 名成员' }, + 'auto.components.TaskPage.manySelectedMembers': { zh: '{{count}} 名成员' }, + 'auto.components.TaskPage.oneSelectedLabel': { zh: '{{count}} 个标签' }, + 'auto.components.TaskPage.manySelectedLabels': { zh: '{{count}} 个标签' }, + 'auto.components.TaskPage.startWorkspaceFromGitLabMR': { + zh: '从 MR {{number}} 启动工作区' + }, + 'auto.components.TaskPage.startWorkspaceFromGitLabIssue': { + zh: '从议题 {{number}} 启动工作区' + }, + 'auto.components.right.sidebar.SourceControl.failedToDiscardOneFile': { + zh: '放弃 {{count}} 个文件的更改失败' + }, + 'auto.components.right.sidebar.SourceControl.failedToDiscardManyFiles': { + zh: '放弃 {{count}} 个文件的更改失败' + }, + 'auto.components.right.sidebar.SourceControl.oneNote': { zh: '{{count}} 条笔记' }, + 'auto.components.right.sidebar.SourceControl.manyNotes': { zh: '{{count}} 条笔记' }, + 'auto.components.right.sidebar.SourceControl.abortRebaseTitle': { zh: '中止变基?' }, + 'auto.components.right.sidebar.SourceControl.abortRebaseDescription': { + zh: '这将取消正在进行的变基,并可能丢失本次变基中已完成的冲突解决结果。' + }, + 'auto.components.right.sidebar.SourceControl.abortRebaseConfirmLabel': { zh: '中止变基' }, + 'auto.components.right.sidebar.SourceControl.abortRebaseFailed': { zh: '中止变基失败' }, + 'auto.components.right.sidebar.SourceControl.failedToAbortRebase': { zh: '无法中止变基' }, + 'auto.components.right.sidebar.SourceControl.abortMergeTitle': { zh: '中止合并?' }, + 'auto.components.right.sidebar.SourceControl.abortMergeDescription': { + zh: '这将取消正在进行的合并,并可能丢失本次合并中已完成的冲突解决结果。' + }, + 'auto.components.right.sidebar.SourceControl.abortMergeConfirmLabel': { zh: '中止合并' }, + 'auto.components.right.sidebar.SourceControl.abortMergeFailed': { zh: '中止合并失败' }, + 'auto.components.right.sidebar.SourceControl.failedToAbortMerge': { zh: '无法中止合并' }, + + // Settings, onboarding, ports, and uploads. + 'auto.components.settings.SshPane.syncedOneServer': { zh: '已同步 {{count}} 台服务器' }, + 'auto.components.settings.SshPane.syncedManyServers': { zh: '已同步 {{count}} 台服务器' }, + 'auto.components.sidebar.AddRemoteHostDialog.sshImportSyncedOneHost': { + zh: '已同步 {{count}} 台 SSH 主机。' + }, + 'auto.components.sidebar.AddRemoteHostDialog.sshImportSyncedManyHosts': { + zh: '已同步 {{count}} 台 SSH 主机。' + }, + 'auto.components.onboarding.IntegrationsStep.oneWorkspaceLinked': { + zh: '已关联 {{count}} 个工作区。可随时添加其他工作区,或替换受限密钥。' + }, + 'auto.components.onboarding.IntegrationsStep.manyWorkspacesLinked': { + zh: '已关联 {{count}} 个工作区。可随时添加其他工作区,或替换受限密钥。' + }, + 'auto.components.settings.task.tracker.integration.cards.oneJiraSiteConnected': { + zh: '已连接 {{count}} 个站点' + }, + 'auto.components.settings.task.tracker.integration.cards.manyJiraSitesConnected': { + zh: '已连接 {{count}} 个站点' + }, + 'auto.components.settings.task.tracker.integration.cards.oneLinearWorkspaceConnected': { + zh: '已连接 {{count}} 个工作区' + }, + 'auto.components.settings.task.tracker.integration.cards.manyLinearWorkspacesConnected': { + zh: '已连接 {{count}} 个工作区' + }, + 'auto.components.terminal.pane.terminal.drop.handler.skippedOneSymlink': { + zh: '已跳过 {{count}} 个符号链接。' + }, + 'auto.components.terminal.pane.terminal.drop.handler.skippedManySymlinks': { + zh: '已跳过 {{count}} 个符号链接。' + }, + 'auto.components.terminal.pane.terminal.drop.handler.skippedOneItem': { + zh: '已跳过 {{count}} 项。' + }, + 'auto.components.terminal.pane.terminal.drop.handler.skippedManyItems': { + zh: '已跳过 {{count}} 项。' + }, + 'auto.components.terminal.pane.terminal.drop.handler.failedToUploadOneFile': { + zh: '上传 {{count}} 个文件失败。' + }, + 'auto.components.terminal.pane.terminal.drop.handler.failedToUploadManyFiles': { + zh: '上传 {{count}} 个文件失败。' + }, + 'auto.components.terminal.pane.terminal.drop.handler.uploadingOneFileToRuntime': { + zh: '正在向运行环境上传 {{count}} 个文件…' + }, + 'auto.components.terminal.pane.terminal.drop.handler.uploadingManyFilesToRuntime': { + zh: '正在向运行环境上传 {{count}} 个文件…' + }, + 'auto.components.terminal.pane.terminal.drop.handler.uploadingOneFileToRemote': { + zh: '正在向远程主机上传 {{count}} 个文件…' + }, + 'auto.components.terminal.pane.terminal.drop.handler.uploadingManyFilesToRemote': { + zh: '正在向远程主机上传 {{count}} 个文件…' + }, + 'auto.components.GitLabItemDialog.approvedWithRequirement': { + zh: '已批准 · 共需 {{requiredCount}} 次批准' + }, + 'auto.components.GitLabItemDialog.oneApprovalRemainingOfRequired': { + zh: '还需 {{remainingCount}} 次批准(共需 {{requiredCount}} 次)' + }, + 'auto.components.GitLabItemDialog.manyApprovalsRemainingOfRequired': { + zh: '还需 {{remainingCount}} 次批准(共需 {{requiredCount}} 次)' + }, + 'auto.components.GitLabItemDialog.oneApprovalRemaining': { + zh: '还需 {{remainingCount}} 次批准' + }, + 'auto.components.GitLabItemDialog.manyApprovalsRemaining': { + zh: '还需 {{remainingCount}} 次批准' + }, + 'auto.components.onboarding.RepoStepNestedImportPanel.scanningFoundOneRepository': { + zh: '扫描中… 已在此文件夹中发现 {{count}} 个仓库。' + }, + 'auto.components.onboarding.RepoStepNestedImportPanel.scanningFoundManyRepositories': { + zh: '扫描中… 已在此文件夹中发现 {{count}} 个仓库。' + }, + 'auto.components.onboarding.RepoStepNestedImportPanel.foundOneRepository': { + zh: '已在此文件夹中发现 {{count}} 个仓库。' + }, + 'auto.components.onboarding.RepoStepNestedImportPanel.foundManyRepositories': { + zh: '已在此文件夹中发现 {{count}} 个仓库。' + }, + 'auto.lib.launch.agent.in.new.tab.promptDeliveryTimedOut': { + zh: '提示词未能发送;请在智能体就绪后手动粘贴。' + }, + 'auto.lib.launch.agent.in.new.tab.notesDeliveryTimedOut': { + zh: '笔记未能发送;请在智能体就绪后手动粘贴。' + }, + 'auto.components.floating.terminal.FloatingTerminalToggleButton.minimizeFloatingWorkspaceWithShortcut': + { + zh: '最小化浮动工作区({{shortcut}})' + }, + 'auto.components.floating.terminal.FloatingTerminalToggleButton.showFloatingWorkspaceWithShortcut': + { + zh: '显示浮动工作区({{shortcut}})' + }, + 'auto.components.status.bar.PortsStatusSegment.oneWorkspacePortAriaLabel': { + zh: '端口:{{workspaceCount}} 个工作区端口' + }, + 'auto.components.status.bar.PortsStatusSegment.manyWorkspacePortsAriaLabel': { + zh: '端口:{{workspaceCount}} 个工作区端口' + }, + 'auto.components.status.bar.PortsStatusSegment.oneWorkspacePortTooltip': { + zh: '端口:{{workspaceCount}} 个工作区端口' + }, + 'auto.components.status.bar.PortsStatusSegment.manyWorkspacePortsTooltip': { + zh: '端口:{{workspaceCount}} 个工作区端口' + }, + 'auto.components.status.bar.PortsStatusSegment.oneWorkspacePortWithExternalTooltip': { + zh: '端口:{{workspaceCount}} 个工作区端口 · {{externalCount}} 个外部端口' + }, + 'auto.components.status.bar.PortsStatusSegment.manyWorkspacePortsWithExternalTooltip': { + zh: '端口:{{workspaceCount}} 个工作区端口 · {{externalCount}} 个外部端口' + }, + + // Conflict status and file operations. + 'auto.components.editor.ConflictComponents.ec7ef38d67': { zh: '未解决' }, + 'auto.components.editor.ConflictComponents.485ab716f0': { zh: '已在本地解决' }, + 'auto.components.editor.ConflictComponents.71a2c63628': { zh: '双方均已修改' }, + 'auto.components.editor.ConflictComponents.ba602cdc05': { zh: '双方均已添加' }, + 'auto.components.editor.ConflictComponents.1c55f94973': { zh: '我方已删除' }, + 'auto.components.editor.ConflictComponents.9d8757e1ca': { zh: '对方已删除' }, + 'auto.components.editor.ConflictComponents.36bceb6bb5': { zh: '我方已添加' }, + 'auto.components.editor.ConflictComponents.5259d6908a': { zh: '对方已添加' }, + 'auto.components.editor.ConflictComponents.be2b3274fe': { zh: '双方均已删除' }, + 'auto.components.editor.ConflictComponents.b119865c75': { zh: '解决冲突标记' }, + 'auto.components.editor.ConflictComponents.5e89c1d824': { + zh: '选择要保留的版本,或合并两个版本' + }, + 'auto.components.editor.ConflictComponents.7e6a9cd8ba': { zh: '决定是否恢复此文件' }, + 'auto.components.editor.ConflictComponents.9d73608d56': { + zh: '决定保留此文件还是接受删除' + }, + 'auto.components.editor.ConflictComponents.2ff72311cd': { + zh: '检查并决定是否保留新增文件' + }, + 'auto.components.editor.ConflictComponents.7e15338be9': { + zh: '保留前请检查对方添加的文件' + }, + 'auto.components.editor.ConflictComponents.a63bf70fc7': { + zh: '请先在 Git 中解决此冲突,或恢复任一版本后再编辑' + }, + 'auto.lib.launch.work.item.direct.agent.20015b902a': { + zh: '智能体启动时间过长。工作区已就绪,请在智能体空闲时粘贴提示词。' + }, + 'auto.lib.launch.work.item.direct.agent.33640ddfa0': { + zh: '智能体启动时间过长。工作区已就绪,请在智能体空闲时粘贴工作项上下文。' + }, + 'auto.components.right.sidebar.file.deletion.localized.copy.222a8a3fb3': { + zh: '删除“{{value0}}”失败。' + }, + 'auto.components.right.sidebar.file.deletion.localized.copy.e9605383fd': { + zh: '将“{{value0}}”移至回收站失败。' + }, + 'auto.components.right.sidebar.file.deletion.localized.copy.f5fbc0837d': { + zh: '将“{{value0}}”移至废纸篓失败。' + }, + 'auto.components.right.sidebar.file.deletion.localized.copy.f583bf91df': { + zh: '远程文件和文件夹会被永久删除且无法撤销;本地文件和文件夹会移至回收站。' + }, + 'auto.components.right.sidebar.file.deletion.localized.copy.b1bde1b2e4': { + zh: '远程文件和文件夹会被永久删除且无法撤销;本地文件和文件夹会移至废纸篓。' + }, + 'auto.components.right.sidebar.useFileExplorerImport.4222b43cc1': { + zh: '导入 {{count}} 个文件失败。' + }, + 'auto.components.right.sidebar.useFileExplorerImport.576340ce05': { + zh: '导入 {{count}} 个文件失败。' + }, + 'auto.components.right.sidebar.useFileExplorerImport.d68e492480': { + zh: '已跳过 {{count}} 个文件。' + }, + 'auto.components.right.sidebar.useFileExplorerImport.17be0f9168': { + zh: '已跳过 {{count}} 个文件。' + }, + 'auto.components.right.sidebar.useFileExplorerImport.58220fc4bb': { + zh: '导入文件失败。' + }, + 'auto.components.right.sidebar.source.control.dropdown.items.ac19d0917c': { + zh: '中止变基' + }, + 'auto.components.right.sidebar.source.control.dropdown.items.515a94f9f6': { + zh: '中止合并' + }, + 'auto.components.right.sidebar.source.control.dropdown.items.f48d7f6445': { + zh: '操作正在进行…' + }, + 'auto.components.right.sidebar.source.control.dropdown.items.62e79d363d': { + zh: '中止正在进行的变基' + }, + 'auto.components.right.sidebar.source.control.dropdown.items.4ef3aa6987': { + zh: '中止正在进行的合并' + }, + 'auto.components.right.sidebar.diff.comments.clear.dialog.state.f07232e970': { + zh: '清除该工作区中的 {{count}} 条笔记?' + }, + 'auto.components.right.sidebar.diff.comments.clear.dialog.state.d10b0441e0': { + zh: '清除该工作区中的 {{count}} 条笔记?' + }, + 'auto.components.right.sidebar.diff.comments.clear.dialog.state.bd98da62a0': { + zh: '清除 {{value0}} 中的 {{count}} 条笔记?' + }, + 'auto.components.right.sidebar.diff.comments.clear.dialog.state.edd639023e': { + zh: '清除 {{value0}} 中的 {{count}} 条笔记?' + }, + + // Hosted review state copy. + 'auto.components.right.sidebar.HostedReviewActions.pullRequestLabel': { zh: '拉取请求' }, + 'auto.components.right.sidebar.HostedReviewActions.mergeRequestLabel': { zh: '合并请求' }, + 'auto.components.right.sidebar.HostedReviewActions.57fb10cfe7': { + zh: '关闭 {{value0}}?' + }, + 'auto.components.right.sidebar.HostedReviewActions.b982b128c0': { + zh: '重新打开 {{value0}}?' + }, + 'auto.components.right.sidebar.HostedReviewActions.a3d572a4de': { + zh: '这会关闭{{value0}}。' + }, + 'auto.components.right.sidebar.HostedReviewActions.78f5ff294c': { + zh: '这会重新打开{{value0}}。' + }, + 'auto.components.right.sidebar.HostedReviewActions.73e5cc9c99': { zh: '关闭' }, + 'auto.components.right.sidebar.HostedReviewActions.641c8ec02a': { zh: '重新打开' }, + 'auto.components.right.sidebar.HostedReviewActions.72c9a274a7': { + zh: '关闭{{value0}}失败。' + }, + 'auto.components.right.sidebar.HostedReviewActions.93afca5395': { + zh: '重新打开{{value0}}失败。' + }, + 'auto.components.right.sidebar.HostedReviewActions.reviewClosedToast': { + zh: '{{value0}} 已关闭' + }, + 'auto.components.right.sidebar.HostedReviewActions.377269db6f': { + zh: '{{value0}} 已重新打开' + }, + + // Remaining count announcements and localized labels. + 'auto.components.editor.CombinedDiffViewer.showOneAiNote': { + zh: '显示 {{count}} 条 AI 笔记' + }, + 'auto.components.editor.CombinedDiffViewer.showManyAiNotes': { + zh: '显示 {{count}} 条 AI 笔记' + }, + 'auto.components.WorktreeJumpPalette.oneResultFoundWithCreateAction': { + zh: '找到 1 个结果;可以创建工作树' + }, + 'auto.components.WorktreeJumpPalette.oneResultFound': { zh: '找到 1 个结果' }, + 'auto.components.WorktreeJumpPalette.manyResultsFoundWithCreateAction': { + zh: '找到 {{count}} 个结果;可以创建工作树' + }, + 'auto.components.WorktreeJumpPalette.manyResultsFound': { + zh: '找到 {{count}} 个结果' + }, + 'auto.components.WorktreeJumpPalette.oneItemAvailableWithCreateAction': { + zh: '有 1 项可用;可以创建工作树' + }, + 'auto.components.WorktreeJumpPalette.oneItemAvailable': { zh: '有 1 项可用' }, + 'auto.components.WorktreeJumpPalette.manyItemsAvailableWithCreateAction': { + zh: '有 {{count}} 项可用;可以创建工作树' + }, + 'auto.components.WorktreeJumpPalette.manyItemsAvailable': { + zh: '有 {{count}} 项可用' + }, + 'auto.components.LinearIssueWorkspace.localCommentAuthor': { zh: '你' }, + 'auto.components.LinearIssueWorkspace.identifierCopyLabel': { zh: '标识符' }, + 'auto.components.LinearIssueWorkspace.suggestedBranchNameCopyLabel': { + zh: '建议的分支名称' + }, + 'auto.components.LinearIssueWorkspace.promptCopyLabel': { zh: '提示词' }, + 'auto.components.JiraIssueWorkspace.localCommentAuthor': { zh: '你' }, + 'auto.components.JiraIssueWorkspace.keyCopyLabel': { zh: '议题编号' }, + 'auto.components.JiraIssueWorkspace.branchNameCopyLabel': { zh: '分支名称' }, + 'auto.components.JiraIssueWorkspace.promptCopyLabel': { zh: '提示词' }, + 'auto.components.LinearItemDrawer.localCommentAuthor': { zh: '你' } +} diff --git a/config/scripts/locale-zh-manual-mobile-emulator-key-overrides.mjs b/config/scripts/locale-zh-manual-mobile-emulator-key-overrides.mjs new file mode 100644 index 000000000000..daa73a8f732f --- /dev/null +++ b/config/scripts/locale-zh-manual-mobile-emulator-key-overrides.mjs @@ -0,0 +1,22 @@ +// Human-reviewed Simplified Chinese for mobile emulator availability and status surfaces. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_MOBILE_EMULATOR_KEY_OVERRIDES = { + // Additional key-specific corrections found during full-catalog human review. + 'auto.lib.ensure.simulator.tab.372d21d428': { zh: '移动端模拟器' }, + + 'auto.components.emulator.pane.mobile.emulator.hidden.toast.c46c979c1d': { + zh: '如需重新启用移动端模拟器,请前往' + }, + + 'auto.components.emulator.pane.emulator.unavailable.pane.f630b9ca9f': { + zh: '移动端模拟器需要安装 Xcode 和 iOS Simulator 运行时的 Mac。在 Linux 或 Windows 上,请使用实体设备或远程 Mac 构建主机。' + }, + + 'auto.components.emulator.pane.emulator.unavailable.pane.b2c268a0b9': { + zh: '移动端模拟器仅支持 macOS' + }, + + 'auto.components.emulator.pane.use.emulator.frame.stream.f1c0179002': { + zh: '视频流未输出画面。' + } +} diff --git a/config/scripts/locale-zh-manual-product-surface-key-overrides.mjs b/config/scripts/locale-zh-manual-product-surface-key-overrides.mjs new file mode 100644 index 000000000000..2ab1e3bf7092 --- /dev/null +++ b/config/scripts/locale-zh-manual-product-surface-key-overrides.mjs @@ -0,0 +1,23 @@ +import { ZH_MANUAL_SOURCE_CONTROL_KEY_OVERRIDES } from './locale-zh-manual-source-control-key-overrides.mjs' +import { ZH_MANUAL_EDITOR_KEY_OVERRIDES } from './locale-zh-manual-editor-key-overrides.mjs' +import { ZH_MANUAL_WORK_ITEM_KEY_OVERRIDES } from './locale-zh-manual-work-item-key-overrides.mjs' +import { ZH_MANUAL_USAGE_ANALYTICS_KEY_OVERRIDES } from './locale-zh-manual-usage-analytics-key-overrides.mjs' +import { ZH_MANUAL_AUTOMATION_KEY_OVERRIDES } from './locale-zh-manual-automation-key-overrides.mjs' +import { ZH_MANUAL_ACTIVITY_SKILLS_REPOSITORY_WORKSPACE_CRASH_KEY_OVERRIDES } from './locale-zh-manual-activity-skills-repository-workspace-crash-key-overrides.mjs' +import { ZH_MANUAL_RUNTIME_CLIENT_KEY_OVERRIDES } from './locale-zh-manual-runtime-client-key-overrides.mjs' +import { ZH_MANUAL_APP_SHELL_KEY_OVERRIDES } from './locale-zh-manual-app-shell-key-overrides.mjs' +import { ZH_MANUAL_MOBILE_EMULATOR_KEY_OVERRIDES } from './locale-zh-manual-mobile-emulator-key-overrides.mjs' + +// Human-reviewed Simplified Chinese overrides grouped by product domain. +// Why: key-level context avoids corrupting code tokens through broad phrase replacement. +export const ZH_MANUAL_PRODUCT_SURFACE_KEY_OVERRIDES = { + ...ZH_MANUAL_SOURCE_CONTROL_KEY_OVERRIDES, + ...ZH_MANUAL_EDITOR_KEY_OVERRIDES, + ...ZH_MANUAL_WORK_ITEM_KEY_OVERRIDES, + ...ZH_MANUAL_USAGE_ANALYTICS_KEY_OVERRIDES, + ...ZH_MANUAL_AUTOMATION_KEY_OVERRIDES, + ...ZH_MANUAL_ACTIVITY_SKILLS_REPOSITORY_WORKSPACE_CRASH_KEY_OVERRIDES, + ...ZH_MANUAL_RUNTIME_CLIENT_KEY_OVERRIDES, + ...ZH_MANUAL_APP_SHELL_KEY_OVERRIDES, + ...ZH_MANUAL_MOBILE_EMULATOR_KEY_OVERRIDES +} diff --git a/config/scripts/locale-zh-manual-runtime-client-key-overrides.mjs b/config/scripts/locale-zh-manual-runtime-client-key-overrides.mjs new file mode 100644 index 000000000000..74ae08a5c941 --- /dev/null +++ b/config/scripts/locale-zh-manual-runtime-client-key-overrides.mjs @@ -0,0 +1,61 @@ +// Human-reviewed Simplified Chinese for shared runtime libraries, web client, and native chat. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_RUNTIME_CLIENT_KEY_OVERRIDES = { + // Shared libraries, web client, and native chat. + 'auto.lib.agent.skill.cli.prerequisite.e99d7dc36f': { zh: 'Orca CLI 注册需要处理' }, + 'auto.lib.fix.checks.agent.launch.4c7f783a7a': { + zh: '已保存的检查修复智能体在此工作区主机上不可用。' + }, + 'auto.lib.fix.checks.agent.launch.9f00d7df0c': { + zh: '修复检查的提示词为空。请更新源代码管理 AI 设置。' + }, + 'auto.lib.floating.workspace.tab.creation.f3785eddc2': { zh: '新建浏览器标签页' }, + 'auto.lib.launch.agent.background.session.4ca0651d56': { + zh: '自动化提示词未发送。请打开工作区并粘贴。' + }, + 'auto.lib.launch.work.item.direct.8bc45efdbc': { zh: '无法解析 PR 源分支引用。' }, + 'auto.lib.orchestration.usage.examples.5e0d489fe1': { zh: '移交进行中的任务' }, + 'auto.lib.orchestration.usage.examples.bddc4c09b8': { zh: '运行分阶段工作流' }, + 'auto.lib.pr.comment.audience.a7150a17bc': { zh: '真人' }, + 'auto.lib.pr.comment.audience.empty.human': { zh: '暂无真人评论。' }, + 'auto.lib.source.control.agent.action.plan.46f1a2c9bd': { zh: '命令输入为空。' }, + 'auto.lib.source.control.generation.plan.dc480d5897': { zh: '命令输入为空。' }, + 'auto.lib.sparse.preset.draft.5915a0a1f6': { + zh: '请使用仓库相对目录,不要使用根目录、绝对路径或上级目录段。' + }, + 'auto.lib.terminal.shortcut.capture.notification.0ab0cd001a': { + zh: 'size-4 text-muted-foreground' + }, + 'auto.lib.terminal.shortcut.capture.notification.141ad6c004': { zh: '已处理终端快捷键' }, + 'auto.lib.terminal.shortcut.capture.notification.b0536028c9': { zh: '打开快捷键设置' }, + 'auto.lib.workspace.create.error.format.37cf0bc991': { + zh: 'Orca 无法为此工作区解析可用的基准引用。' + }, + 'auto.web.WebConnect.3affe7de3a': { zh: '粘贴此浏览器可访问的 Orca 服务器配对 URL。' }, + 'auto.web.WebConnect.mobileScopeRejected': { + zh: '此二维码仅授予受限的移动端访问权限。若要使用完整 Web 应用,请前往“设置 → 运行环境 → 分享此 Orca 服务器 → 新建链接”,打开浏览器访问链接。' + }, + 'auto.web.web.preload.api.0a69fcd8bc': { + zh: 'platforms 必须是包含 darwin、linux 或 win32 字段的对象。' + }, + 'auto.web.web.preload.api.10898045f3': { + zh: '已忽略“{{value0}}”的快捷键:请使用字符串数组。' + }, + 'auto.web.web.preload.api.31bfe8ae1a': { zh: 'Web 客户端中不可用。' }, + 'auto.web.web.preload.api.36761d9604': { zh: '已忽略未知的快捷键操作“{{value0}}”。' }, + 'auto.web.web.preload.api.8dfcb7a351': { zh: 'Web 客户端不支持区域截图。' }, + 'auto.web.web.preload.api.9fc90740b6': { zh: 'Web 客户端不支持生成提交信息。' }, + 'auto.web.web.preload.api.b8a1618172': { zh: 'Web 客户端不支持生成 PR 详细信息。' }, + 'auto.web.web.preload.api.e57c82d276': { zh: 'Web 客户端不支持检测提交信息模型。' }, + 'auto.web.web.preload.api.fb290366b2': { zh: 'Web 端不可用。' }, + 'auto.web.webPreloadApi.aiVaultUnavailableForHost': { + zh: '此执行主机不支持智能体会话历史。' + }, + 'components.native-chat.composer.pastedImageLabel': { zh: '已粘贴的图片' }, + 'components.native-chat.composer.worktreeNotReady': { zh: '工作树尚未就绪,请稍后重试。' }, + 'components.native-chat.state.error.subtitle': { + zh: '无法读取对话记录。请切回终端继续工作。' + }, + 'components.native-chat.state.loading.subtitle': { zh: '正在读取智能体对话记录。' }, + 'components.native-chat.state.notAgent.title': { zh: '此处没有对话' } +} diff --git a/config/scripts/locale-zh-manual-settings-core-key-overrides.mjs b/config/scripts/locale-zh-manual-settings-core-key-overrides.mjs new file mode 100644 index 000000000000..c7291b836fd5 --- /dev/null +++ b/config/scripts/locale-zh-manual-settings-core-key-overrides.mjs @@ -0,0 +1,477 @@ +// Human-reviewed Simplified Chinese overrides for core settings and search metadata. +// Key-specific entries keep code literals intact and resolve wording that needs call-site context. +export const ZH_MANUAL_SETTINGS_CORE_KEY_OVERRIDES = { + // Settings navigation and status bar + 'settings.appearance.statusBar.claudeToggleDescription': { + zh: '显示当前工作区的 Claude 令牌用量和费用。' + }, + 'settings.appearance.statusBar.codexToggleDescription': { + zh: '显示当前工作区的 Codex 令牌用量和费用。' + }, + 'settings.appearance.statusBar.geminiToggleDescription': { + zh: '显示当前工作区的 Gemini 令牌用量和费用。' + }, + 'settings.appearance.statusBar.opencodeGoToggleDescription': { + zh: '显示当前工作区的 OpenCode Go 令牌用量和费用。' + }, + 'settings.appearance.statusBar.portsToggleDescription': { + zh: '显示工作区的活动端口。点击可查看工作区端口和外部监听地址。' + }, + 'auto.hooks.useSettingsNavigationMetadata.0059bd17f3': { zh: '允许智能体控制电脑上的任意应用。' }, + 'auto.hooks.useSettingsNavigationMetadata.09607cb0fe': { zh: 'Git 与源代码管理' }, + 'auto.hooks.useSettingsNavigationMetadata.1e761cff2b': { zh: '移动端模拟器' }, + 'auto.hooks.useSettingsNavigationMetadata.2cd4ea75da': { zh: '工作区默认设置、应用配置与维护。' }, + 'auto.hooks.useSettingsNavigationMetadata.2d0659f6f0': { + zh: '全局终端、浏览器和 Markdown 标签页。' + }, + 'auto.hooks.useSettingsNavigationMetadata.33a5e1d597': { + zh: '连接 GitHub、GitLab、Linear 及其他代码托管服务。' + }, + 'auto.hooks.useSettingsNavigationMetadata.3d65d3f1b9': { + zh: '为 Orca 和编码智能体配置移动端模拟器支持。' + }, + 'auto.hooks.useSettingsNavigationMetadata.4121f7a0a2': { + zh: '管理智能体、设置默认智能体并自定义命令。' + }, + 'auto.hooks.useSettingsNavigationMetadata.42ae40842f': { + zh: '保存的终端命令,可全局使用或限定到单个项目。' + }, + 'auto.hooks.useSettingsNavigationMetadata.5235c215ca': { + zh: '选择要在“任务”页面和侧边栏中显示的任务来源。' + }, + 'auto.hooks.useSettingsNavigationMetadata.5f32ac08f3': { zh: '完成 Orca 核心工作流的入门清单。' }, + 'auto.hooks.useSettingsNavigationMetadata.7682607591': { + zh: '在智能体或终端有事件时发送原生桌面通知。' + }, + 'auto.hooks.useSettingsNavigationMetadata.8ac3de82f5': { zh: '使用设备端模型进行本地语音转写。' }, + 'auto.hooks.useSettingsNavigationMetadata.95a1886d94': { zh: '通过手机控制终端和智能体。' }, + 'auto.hooks.useSettingsNavigationMetadata.b11a5a48a2': { + zh: '主题、缩放、应用与终端外观、侧边栏和状态栏。' + }, + 'auto.hooks.useSettingsNavigationMetadata.b351014180': { + zh: 'Orca 统计,以及 Claude、Codex、OpenCode 的令牌分析和 Grok 订阅用量。' + }, + 'auto.hooks.useSettingsNavigationMetadata.b49abbd2f7': { zh: '智能体' }, + 'auto.hooks.useSettingsNavigationMetadata.cd50cec5d7': { zh: '通过 Orca 协调多个编码智能体。' }, + 'auto.hooks.useSettingsNavigationMetadata.e338c507c1': { zh: '用于故障排除的底层兼容性设置。' }, + 'auto.hooks.useSettingsNavigationMetadata.e815fd01bd': { + zh: '主页、链接打开方式和会话 Cookie。' + }, + 'auto.hooks.useSettingsNavigationMetadata.f70ac54d38': { zh: 'AI 提供商账户' }, + + // Core settings surfaces + 'auto.components.settings.AccountsPane.3d245ef7d9': { zh: 'Codex 报告此登录已失效' }, + 'auto.components.settings.AccountsPane.316ca4e610': { zh: '删除 Cookie' }, + 'auto.components.settings.AccountsPane.53f7b8c7a2': { zh: '上次刷新:{{value0}}' }, + 'auto.components.settings.AccountsPane.589eba1eee': { zh: '需要重新登录' }, + 'auto.components.settings.AccountsPane.79418c782a': { + zh: '在浏览器中打开 platform.minimax.io/console/usage 并登录,然后在 DevTools 的“网络”面板中选择任意 remains 请求,复制 Cookie 请求头。' + }, + 'auto.components.settings.AccountsPane.7ce0e1907c': { + zh: ')。可在浏览器的 DevTools → 网络 → 任意 opencode.ai 请求 → Cookie 请求头中找到。OpenCode Go 采用 Web 登录,Windows 与 WSL 终端共享此登录状态。' + }, + 'auto.components.settings.AccountsPane.854ebbcc45': { + zh: 'Orca 将删除此已保存账户的托管 Claude 身份验证。如果它是当前账户,Orca 将回退到系统默认的 Claude 登录。' + }, + 'auto.components.settings.AccountsPane.a7e38affcd': { + zh: 'Fe26.2**… 令牌或 auth=Fe26.2**… 请求头' + }, + 'auto.components.settings.AccountsPane.b15ce90870': { + zh: '{{value0}} → {{value1}}。继续旧会话前,请重启正在运行的 Claude 终端。' + }, + 'auto.components.settings.AccountsPane.d0d53b7eb0': { + zh: '管理 Orca 用于实时获取速率限制信息的 Codex 账户。' + }, + 'auto.components.settings.AccountsPane.e05d0ff737': { + zh: '使用 {{value0}} 中当前的 Claude 登录。' + }, + 'auto.components.settings.AccountsPane.fcc4093fc1': { + zh: '使用 {{value0}} 中当前的 Codex 登录。' + }, + 'auto.components.settings.AdvancedNetworkSettingsSection.0adfce9fa7': { + zh: '支持 http、https、socks、socks4 和 socks5 URL。' + }, + 'auto.components.settings.AdvancedNetworkSettingsSection.3e431564b5': { + zh: 'localhost, 127.0.0.1, *.internal' + }, + 'auto.components.settings.AdvancedNetworkSettingsSection.d93c7cd531': { + zh: '配置应用级网络路由。' + }, + 'auto.components.settings.AdvancedPane.8b7a8df299': { + zh: '用于技术支持和故障排除的底层兼容性方案。' + }, + 'auto.components.settings.AppearancePane.102d6b5f9b': { zh: 'IDE 字体' }, + 'auto.components.settings.AppearancePane.5db6ba961f': { + zh: '在左侧边栏顶部显示 Orca Mobile 按钮。' + }, + 'auto.components.settings.AppearancePane.61d842eca0': { + zh: '在侧边栏中显示 Orca Mobile 快捷入口。也可随时从工具箱打开。' + }, + 'auto.components.settings.AppearancePane.9868f39007': { zh: '标题栏应用名称' }, + 'auto.components.settings.AppearancePane.ca1590d42f': { zh: '应用图标' }, + 'auto.components.settings.AppearancePane.leftSidebarAppearance.matchTerminal': { zh: '匹配终端' }, + 'auto.components.settings.AppearancePane.leftSidebarAppearance.rowDescription': { + zh: '让左侧边栏匹配终端、保持默认外观或使用自定义色调。' + }, + 'auto.components.settings.AppearancePane.terminalTitle': { zh: '终端' }, + 'auto.components.settings.CommitMessageAiPane.15b60d54b2': { + zh: '例如:ollama run llama3.1 {prompt}' + }, + 'auto.components.settings.CommitMessageAiPane.25350d670f': { zh: '自定义' }, + 'auto.components.settings.CommitMessageAiPane.34d0348e34': { zh: '生成' }, + 'auto.components.settings.CommitMessageAiPane.4f722a5f53': { + zh: '选择“自定义命令”的提交信息、拉取请求和分支名称方案会使用此设置。使用' + }, + 'auto.components.settings.CommitMessageAiPane.8cd2be0948': { zh: '消息' }, + 'auto.components.settings.CommitMessageAiPane.d5f0de6309': { zh: '打开“创建 PR”时生成详情' }, + 'auto.components.settings.CliSection.8a9b784c60': { zh: '已过期' }, + 'auto.components.settings.CliSection.36a6f919ba': { + zh: '为智能体提供理解 Orca 的工作区、终端和进度工作流。' + }, + 'auto.components.settings.ExperimentalPane.0277901cf7': { + zh: '在左侧边栏添加“智能体”入口,以线程式工作树动态展示已完成的智能体、阻塞问题、未读状态和工作树创建事件。此功能为实验性,事件模型和界面可能会调整。' + }, + 'auto.components.settings.ExperimentalPane.agentHibernation.copy': { + zh: '达到设定的空闲时长后,停止已完成的后台智能体终端;再次打开时恢复受支持的会话。智能体休眠会保留由 Orca 启动的智能体的启动选项;手动启动的智能体可能按当前 Orca 默认设置恢复。安全机制仍在调试,因此该功能为实验性。' + }, + 'auto.components.settings.ExperimentalPane.agentHibernation.description': { + zh: '达到设定的空闲时长后,停止后台智能体终端;再次打开时恢复受支持的会话。' + }, + 'auto.components.settings.ExperimentalPane.agentHibernation.idleMinutesDescription': { + zh: '已完成的后台智能体需要空闲多少分钟,Orca 才会将其休眠。' + }, + 'auto.components.settings.ExperimentalPane.agentHibernation.title': { zh: '智能体休眠' }, + 'auto.components.settings.ExperimentalPane.ec897e8d89': { zh: '终端提醒' }, + 'auto.components.settings.ExperimentalPane.f63ea281e3': { + zh: '以线程形式汇总智能体完成事件和阻塞状态的左侧边栏动态。' + }, + 'auto.components.settings.ExperimentalPane.nativeChat.copy': { + zh: '添加可从受支持的智能体终端窗格切换的原生聊天视图。对话记录准确性、流式响应和终端体验一致性仍在调试,因此该功能为实验性。' + }, + 'auto.components.settings.GeneralEditorSettingsSection.45c6e85c4d': { zh: '编辑器' }, + 'auto.components.settings.GeneralEditorSettingsSection.b492397d34': { + zh: '默认显示 Git 差异时采用的布局。' + }, + 'auto.components.settings.GeneralEditorSettingsSection.b82f86d7d2': { + zh: '富文本 Markdown 拼写检查' + }, + 'auto.components.settings.GeneralEditorSettingsSection.f80603d293': { + zh: '在富文本编辑器中显示本地 Markdown 笔记控件和智能体交接操作。' + }, + 'auto.components.settings.GeneralSupportSection.511782265b': { + zh: '在 GitHub 上为项目加星以示支持。' + }, + 'auto.components.settings.GeneralSupportSection.5c49f02662': { zh: '已隐藏' }, + 'auto.components.settings.GeneralSupportSection.6922c1fa2b': { zh: '在 GitHub 上为 Orca 加星' }, + 'auto.components.settings.GeneralUpdateSettingsSection.7173352632': { zh: '空闲' }, + 'auto.components.settings.GeneralUpdateSettingsSection.82465b2444': { zh: '可用' }, + 'auto.components.settings.GeneralUpdateSettingsSection.90eb7309d7': { zh: '不可用' }, + 'auto.components.settings.GeneralUpdateSettingsSection.a0832ccdb1': { zh: '已下载' }, + 'auto.components.settings.GitPane.3072428ac7': { zh: 'glab' }, + 'auto.components.settings.GitPane.813e15b346': { zh: '自定义' }, + 'auto.components.settings.GitPane.b5f534717a': { zh: '共同署名' }, + 'auto.components.settings.GrokAccountsSection.75e396bf42': { + zh: 'Grok 统一计费账户包含的月度用量。' + }, + 'auto.components.settings.GrokAccountsSection.e6dadc1e2b': { zh: '月度用量' }, + 'auto.components.settings.IntegrationsPane.15cf990798': { zh: '未通过身份验证' }, + 'auto.components.settings.IntegrationsPane.1683acbac4': { + zh: '连接 Orca 用于拉取请求、合并请求、检查和评审状态的代码托管平台。' + }, + 'auto.components.settings.IntegrationsPane.1fac9b4910': { zh: '{{value0}} · 拉取请求和提交状态' }, + 'auto.components.settings.IntegrationsPane.277fc23929': { zh: '{{value0}} · 拉取请求和构建状态' }, + 'auto.components.settings.IntegrationsPane.3ba07f933b': { + zh: '连接议题跟踪服务,让 Orca 浏览任务并在新工作区中自动附上相关上下文。' + }, + 'auto.components.settings.IntegrationsPane.45bf5e6e4b': { zh: '身份验证失败' }, + 'auto.components.settings.IntegrationsPane.4972f3c95d': { zh: '已配置' }, + 'auto.components.settings.IntegrationsPane.51000487c4': { zh: 'gh auth login' }, + 'auto.components.settings.IntegrationsPane.6355fe585e': { zh: '已检测仓库的拉取请求和提交状态' }, + 'auto.components.settings.IntegrationsPane.6bd148dcb5': { + zh: '通过 Gitea REST API 获取拉取请求和提交状态。' + }, + 'auto.components.settings.IntegrationsPane.9707523939': { zh: '拉取请求和构建状态' }, + 'auto.components.settings.IntegrationsPane.a3326f6f1b': { zh: 'glab' }, + 'auto.components.settings.IntegrationsPane.ae38fc62a8': { zh: '正常' }, + 'auto.components.settings.IntegrationsPane.e74de656ce': { zh: 'glab auth login' }, + 'auto.components.settings.IntegrationsPane.e7b2dd46f9': { zh: '正在测试…' }, + 'auto.components.settings.KagiSessionLinkForm.ff450194cd': { zh: 'Kagi 私密会话链接' }, + 'auto.components.settings.ManageSessionKillDialog.87dcafc85c': { zh: '结束此会话?' }, + 'auto.components.settings.McpConfigFileRow.845ae248e8': { zh: '有效' }, + 'auto.components.settings.McpConfigFileRow.e720c139cd': { zh: '打开' }, + 'auto.components.settings.NotificationsPane.4aa5085cd7': { zh: '自定义:' }, + 'auto.components.settings.OpenInMenuSetting.c1d817e027': { zh: '已添加' }, + 'auto.components.settings.QuickCommandsPane.9b3e338d62': { zh: 'Enter' }, + 'auto.components.settings.QuickCommandsPane.8c877dec41': { zh: '全局' }, + 'auto.components.settings.RecentTabOrderControl.6e6a3fcc61': { zh: '最近使用' }, + 'auto.components.settings.RepositoryHooksSection.2d03a514db': { zh: '本地' }, + 'auto.components.settings.RepositoryHooksSection.39da2ae12f': { zh: 'orca.yaml' }, + 'auto.components.settings.RepositoryHooksSection.bbbd6e0bc4': { zh: '命令来源与 orca.yaml' }, + 'auto.components.settings.RepositoryHooksSection.8dbe6bedf5': { zh: '无效' }, + 'auto.components.settings.RepositoryHooksSection.0518758f38': { zh: '两者' }, + 'auto.components.settings.RepositoryHooksSection.175daba180': { zh: '示例' }, + 'auto.components.settings.RepositoryIconPicker.2d8bd302fa': { zh: '头像' }, + 'auto.components.settings.RepositorySourceControlAiActionRows.1cd88d470a': { zh: '自定义' }, + 'auto.components.settings.RepositorySourceControlAiCustomCommand.f9941f0caf': { + zh: '例如:ollama run llama3.1 {prompt}' + }, + 'auto.components.settings.RuntimeEnvironmentsPane.54dee18f5c': { zh: '隐藏表单' }, + 'auto.components.settings.RuntimePairingUrlGenerator.13704d635e': { + zh: '已复制 Web 客户端 URL。' + }, + 'auto.components.settings.RuntimePairingUrlGenerator.6dd594a507': { + zh: '已生成 Web 客户端 URL。' + }, + 'auto.components.settings.RuntimePairingUrlGenerator.b91e36a986': { zh: 'Web' }, + 'auto.components.settings.Settings.e1578cd4bc': { zh: '工作流' }, + 'auto.components.settings.Settings.6855b0f77d': { zh: '完成 Orca 并行智能体工作的核心工作流。' }, + 'auto.components.settings.SettingsFormControls.cb330ef7f8': { zh: ' / {{value0}}' }, + 'auto.components.settings.SourceControlAiActionRecipeDefaults.db9bd75d10': { zh: '参数' }, + 'auto.components.settings.SourceControlActionRepoOverrideNote.reviewFirst': { zh: '先查看' }, + 'auto.components.settings.SourceControlActionRepoOverrideNote.review': { zh: '查看' }, + 'auto.components.settings.SparsePresetSettingsSection.a6fcdd9e3c': { zh: '名称' }, + 'auto.components.settings.SparsePresetSettingsSection.68bbcd864a': { zh: '新建' }, + 'auto.components.settings.TerminalInteractionSection.567633ff50': { + zh: '右键点击将剪贴板内容粘贴到终端;按住 Control 点击可打开上下文菜单。' + }, + 'auto.components.settings.TerminalInteractionSection.c64497148a': { + zh: '右键点击可粘贴剪贴板内容;按住 Control 点击可打开上下文菜单。' + }, + 'auto.components.settings.TerminalAppearanceSection.048aac8a64': { zh: '终端排版' }, + 'auto.components.settings.TerminalAppearanceSection.1b79379d4f': { + zh: '控制非活动窗格的变暗程度和拆分分隔线的粗细。' + }, + 'auto.components.settings.TerminalAppearanceSection.2e5aec3cf6': { zh: '下划线' }, + 'auto.components.settings.TerminalAppearanceSection.52854a5608': { zh: '块状' }, + 'auto.components.settings.TerminalAppearanceSection.7233d594bf': { + zh: '为支持连字的字体渲染编程连字(例如 =>、!=、===)。“自动”仅对已知的连字字体启用此功能(Fira Code、JetBrains Mono、Cascadia Code、Iosevka 等)。' + }, + 'auto.components.settings.TerminalAppearanceSection.74736cc9b1': { zh: '闪烁光标' }, + 'auto.components.settings.TerminalAppearanceSection.855a76343a': { zh: '从 Ghostty 导入' }, + 'auto.components.settings.TerminalAppearanceSection.a408266e67': { zh: '字体系列' }, + 'auto.components.settings.TerminalAppearanceSection.abcb4dd019': { zh: '终端光标' }, + 'auto.components.settings.TerminalAppearanceSection.b9f1804422': { zh: '光标不透明度' }, + 'auto.components.settings.TerminalAppearanceSection.bafc80efbc': { zh: '控制终端的行高倍数。' }, + 'auto.components.settings.TerminalAppearanceSection.db270cc9a9': { zh: '光标形状' }, + 'auto.components.settings.TerminalAppearanceSection.e070e8aeba': { zh: '竖线' }, + 'auto.components.settings.TerminalAppearanceSection.e1a5c25555': { zh: '终端窗格' }, + 'auto.components.settings.TerminalAppearanceSection.f27a99978d': { zh: '分隔线粗细' }, + 'auto.components.settings.TerminalPane.0a10420e1a': { zh: '将 Option 用作 Alt' }, + 'auto.components.settings.TerminalPane.1158f8fd55': { zh: '新标签页' }, + 'auto.components.settings.TerminalPane.219aaa59f4': { zh: 'WSL 发行版' }, + 'auto.components.settings.TerminalPane.6c6a054a1c': { zh: '在新标签页中运行' }, + 'auto.components.settings.TerminalPane.9df53f7c14': { zh: '终端历史记录行数' }, + 'auto.components.settings.TerminalPane.ab3a1f9068': { zh: 'wsl.exe' }, + 'auto.components.settings.TerminalPane.adbafefe56': { zh: '自定义' }, + 'auto.components.settings.TerminalPane.badb1219fc': { zh: '两者' }, + 'auto.components.settings.TerminalPane.c1fc9e9444': { zh: 'GPU 加速' }, + 'auto.components.settings.TerminalPane.c73d510938': { zh: '右侧' }, + 'auto.components.settings.TerminalPane.d78fc4fdef': { zh: '正在加载发行版' }, + 'auto.components.settings.TerminalPane.e7aec1fd60': { zh: '左侧' }, + 'auto.components.settings.TerminalPane.f61ac77f16': { zh: 'Git Bash' }, + 'auto.components.settings.TerminalPane.fe20f79dd1': { zh: 'PowerShell 版本' }, + 'auto.components.settings.TerminalPane.scrollSpeed.normalDescription': { + zh: '滚轮滚动终端历史记录的倍数。' + }, + 'auto.components.settings.TerminalSettingsPreview.d06664e889': { zh: '深色' }, + 'auto.components.settings.TerminalWindowSection.0cb4459fb8': { zh: '白色' }, + 'auto.components.settings.TerminalWindowSection.1be593d3e8': { zh: 'ANSI 亮色' }, + 'auto.components.settings.TerminalWindowSection.292a4c7316': { zh: '蓝色' }, + 'auto.components.settings.TerminalWindowSection.3a78f30b50': { zh: '红色' }, + 'auto.components.settings.TerminalWindowSection.40c3cfd30a': { zh: '选区背景色' }, + 'auto.components.settings.TerminalWindowSection.68e9f07de0': { zh: 'ANSI 常规色' }, + 'auto.components.settings.TerminalWindowSection.8b450b5305': { zh: '选区前景色' }, + 'auto.components.settings.TerminalWindowSection.8f2092b315': { zh: '绿色' }, + 'auto.components.settings.TerminalWindowSection.907131d741': { zh: '正在重启…' }, + 'auto.components.settings.TerminalWindowSection.a2d9f095a7': { zh: '光标文字' }, + 'auto.components.settings.TerminalWindowSection.adfdee23cb': { zh: '黑色' }, + 'auto.components.settings.TerminalWindowSection.b96ba13ed1': { zh: '窗口' }, + 'auto.components.settings.TerminalWindowSection.bb516de873': { zh: '黄色' }, + 'auto.components.settings.TerminalWindowSection.c9e1fdf42f': { zh: '光标' }, + 'auto.components.settings.TerminalWindowSection.cd0700762b': { zh: '光标颜色' }, + 'auto.components.settings.TerminalWindowSection.cf37ff69f6': { zh: '基础色' }, + 'auto.components.settings.TerminalWindowSection.da64e8f4c1': { zh: '终端背景色' }, + 'auto.components.settings.TerminalWindowSection.fb8c6f1967': { + zh: '粗体文字的颜色。未设置时回退到常规颜色。' + }, + 'auto.components.settings.VoicePane.174da92062': { zh: '按住' }, + 'auto.components.settings.VoicePane.1ba81c0ff0': { zh: '推荐' }, + 'auto.components.settings.VoicePane.295d84b849': { + zh: '一次开始,再按一次停止。按住模式:仅在按住' + }, + 'auto.components.settings.VoicePane.366e1b4f36': { zh: '即可在当前聚焦的窗格中听写文本。' }, + 'auto.components.settings.VoicePane.7cf715f891': { zh: '时听写。' }, + 'auto.components.settings.VoicePane.d504ab05f0': { zh: '流式' }, + 'auto.components.settings.VoicePane.fbe5990716': { zh: '选择模型' }, + 'auto.components.settings.SshPassphraseDialog.405066423c': { zh: '解锁' }, + 'auto.components.settings.WorktreeSymlinksSection.ea06227efa': { zh: '已添加' }, + 'auto.components.settings.WslCliRegistration.e2b0ee267f': { zh: '已过期' }, + + // Search metadata with literal MT errors + 'auto.components.settings.accounts.search.5b3f18ef4a': { zh: '切换' }, + 'auto.components.settings.accounts.search.7e67d7d1b6': { zh: 'wrk' }, + 'auto.components.settings.accounts.search.bdbd1e668e': { zh: 'Windows' }, + 'auto.components.settings.accounts.search.e02c136ad0': { zh: '身份验证' }, + 'auto.components.settings.accounts.search.7118d2f908': { zh: '凭据' }, + 'auto.components.settings.accounts.search.042885c07c': { zh: '已过期' }, + 'auto.components.settings.agents.search.77c02fa3c3': { zh: 'Windows' }, + 'auto.components.settings.appearance.search.00a028f25f': { zh: '用量' }, + 'auto.components.settings.appearance.search.0c83659f48': { zh: '快捷键' }, + 'auto.components.settings.appearance.search.4ddbde4999': { zh: 'CPU' }, + 'auto.components.settings.appearance.search.d18b54ca90': { zh: 'Dock' }, + 'auto.components.settings.appearance.search.f4997e0f8a': { zh: '连接' }, + 'auto.components.settings.appearance.search.51f957ce39': { zh: '名称' }, + 'auto.components.settings.appearance.search.a278406ed5': { zh: '远程' }, + 'auto.components.settings.appearance.search.e5bc35d59e': { zh: '窗口' }, + 'auto.components.settings.auto.rename.branch.search.a482f6a423': { zh: 'slug' }, + 'auto.components.settings.auto.rename.branch.search.f41833025e': { zh: '生成' }, + 'auto.components.settings.browser.search.0bb34eacc9': { zh: '查询' }, + 'auto.components.settings.browser.search.1c1e097985': { zh: 'Arc' }, + 'auto.components.settings.browser.search.1f8153acfb': { zh: 'DuckDuckGo' }, + 'auto.components.settings.browser.search.29193a51d5': { zh: 'Cookie' }, + 'auto.components.settings.browser.search.0732ebe6fb': { zh: '私密' }, + 'auto.components.settings.browser.search.5164c47e31': { zh: '空白' }, + 'auto.components.settings.browser.search.3910a41f32': { zh: '身份验证' }, + 'auto.components.settings.browser.search.533a253deb': { zh: 'Edge' }, + 'auto.components.settings.browser.search.72c58f7792': { zh: 'WebView' }, + 'auto.components.settings.browser.search.7539f6336c': { zh: '配置文件' }, + 'auto.components.settings.browser.search.75a0d435b7': { zh: 'Chrome' }, + 'auto.components.settings.browser.search.90425d313c': { zh: 'Shift' }, + 'auto.components.settings.browser.search.ad40e75d13': { zh: 'Bing' }, + 'auto.components.settings.browser.search.e1c2a57f07': { zh: 'Kagi' }, + 'auto.components.settings.browser.use.search.088e7a9012': { zh: 'Chrome' }, + 'auto.components.settings.browser.use.search.2e1b09897b': { zh: 'Edge' }, + 'auto.components.settings.browser.use.search.6ea88e5206': { zh: 'npx' }, + 'auto.components.settings.browser.use.search.7e0dcb257a': { zh: 'Shell' }, + 'auto.components.settings.browser.use.search.96ce3d2de2': { zh: '身份验证' }, + 'auto.components.settings.browser.use.search.ab349a2dd0': { zh: 'Arc' }, + 'auto.components.settings.browser.use.search.fb8178824f': { zh: 'Cookie' }, + 'auto.components.settings.commit.message.ai.search.0f29331fed': { zh: '参数' }, + 'auto.components.settings.commit.message.ai.search.8e9cc598d7': { zh: '生成' }, + 'auto.components.settings.commit.message.ai.search.93e5210da8': { zh: '消息' }, + 'auto.components.settings.computer.use.search.82f01c2d2c': { zh: '无障碍' }, + 'auto.components.settings.developer.permissions.search.00e954319e': { zh: 'Whisper' }, + 'auto.components.settings.developer.permissions.search.08f8039ca9': { zh: '无障碍' }, + 'auto.components.settings.developer.permissions.search.b192432ef0': { zh: '音频' }, + 'auto.components.settings.developer.permissions.search.7f145a3984': { zh: '窗口' }, + 'auto.components.settings.developer.permissions.search.78a10b826f': { zh: 'Bonjour' }, + 'auto.components.settings.developer.permissions.search.f061f08b7b': { zh: 'SoX' }, + 'auto.components.settings.experimental.search.8facf10138': { zh: '铃声' }, + 'auto.components.settings.experimental.search.9af7a518db': { zh: '角色' }, + 'auto.components.settings.experimental.search.4d63251595': { + zh: '以线程形式汇总智能体完成事件和阻塞状态的左侧边栏动态。' + }, + 'auto.components.settings.floating.workspace.search.2b5efa55c9': { zh: '全局' }, + 'auto.components.settings.general.search.2b463f0bf9': { zh: '视图' }, + 'auto.components.settings.general.search.9da6c875e5': { zh: 'Dock' }, + 'auto.components.settings.general.search.e4fb4516d0': { zh: '加星' }, + 'auto.components.settings.general.search.ebf8f056b5': { zh: 'Zed' }, + 'auto.components.settings.general.search.6382fe9724': { zh: 'npx' }, + 'auto.components.settings.general.search.54ba13831a': { zh: '最近' }, + 'auto.components.settings.general.search.ec5049e510': { zh: '嵌套' }, + 'auto.components.settings.general.search.f8f0ac213a': { zh: '顺序' }, + 'auto.components.settings.general.search.f472e97440': { zh: 'Aider' }, + 'auto.components.settings.general.search.fb84767421': { zh: '切换' }, + 'auto.components.settings.general.search.fe62b3f09f': { zh: 'Ctrl' }, + 'auto.components.settings.git.search.769ddd7f81': { zh: '自定义' }, + 'auto.components.settings.git.search.6ee3cfff02': { zh: 'git diff' }, + 'auto.components.settings.git.search.8461c908ae': { zh: '共同署名' }, + 'auto.components.settings.git.search.ead733645f': { zh: 'glab' }, + 'auto.components.settings.jira.integration.card.3df81cb0ac': { zh: '正常' }, + 'auto.components.settings.mobile.emulator.search.25d7bfbcd4': { zh: 'UDID' }, + 'auto.components.settings.mobile.emulator.search.27397fe8e9': { zh: 'Xcode 命令行工具' }, + 'auto.components.settings.mobile.emulator.search.6f728f1456': { zh: '模拟器点击' }, + 'auto.components.settings.mobile.emulator.search.7650063d17': { zh: 'simctl' }, + 'auto.components.settings.mobile.emulator.search.84e5706975': { zh: 'serve-sim' }, + 'auto.components.settings.mobile.emulator.search.ea1f51b980': { + zh: '检查 Xcode、simctl、serve-sim 和模拟器设备是否已就绪。' + }, + 'auto.components.settings.mobile.emulator.search.ec3c4043fd': { zh: '默认 iPad' }, + 'auto.components.settings.mobile.emulator.search.f8b871d655': { zh: '智能体 CLI' }, + 'auto.components.settings.mobile.pane.search.126afc5dbd': { zh: '远程' }, + 'auto.components.settings.mobile.pane.search.16bff559a0': { zh: 'tailnet' }, + 'auto.components.settings.mobile.pane.search.1802188b5d': { zh: 'Wi-Fi' }, + 'auto.components.settings.mobile.pane.search.3c1807a81a': { zh: 'QR' }, + 'auto.components.settings.mobile.pane.search.5e8fda4d7f': { zh: '已配对' }, + 'auto.components.settings.mobile.pane.search.8015fd9523': { zh: '保持' }, + 'auto.components.settings.mobile.pane.search.a023683767': { zh: '接口' }, + 'auto.components.settings.mobile.pane.search.e518cbd61c': { zh: '配对' }, + 'auto.components.settings.mobile.pane.search.fadcbfdd99': { zh: '适配' }, + 'auto.components.settings.mobile.pane.search.c690e3ee38': { zh: 'Tailscale' }, + 'auto.components.settings.mobile.settings.search.6bfa001752': { zh: 'APK' }, + 'auto.components.settings.mobile.settings.search.7e801801ac': { zh: '远程' }, + 'auto.components.settings.mobile.settings.search.a7eececc1d': { zh: 'Android' }, + 'auto.components.settings.mobile.settings.search.cf2c93b479': { zh: '配对' }, + 'auto.components.settings.notifications.search.5f7472d3fb': { zh: '完成' }, + 'auto.components.settings.notifications.search.3014ad1b8f': { zh: 'Ding' }, + 'auto.components.settings.notifications.search.57e34a31cd': { zh: 'WAV' }, + 'auto.components.settings.notifications.search.722face52f': { zh: 'AAC' }, + 'auto.components.settings.notifications.search.6e08f78315': { zh: '音频' }, + 'auto.components.settings.notifications.search.ae0487f8fd': { zh: '铃声' }, + 'auto.components.settings.notifications.search.a4c3b29a3c': { zh: '已聚焦' }, + 'auto.components.settings.notifications.search.adbc3a0fcf': { zh: '原生' }, + 'auto.components.settings.notifications.search.d58b64dddf': { zh: '音量' }, + 'auto.components.settings.notifications.search.ef86a782cc': { zh: 'Bong' }, + 'auto.components.settings.notifications.search.fa60d8e4ab': { zh: '抑制' }, + 'auto.components.settings.orchestration.search.08c65b12a2': { zh: '示例' }, + 'auto.components.settings.orchestration.search.21c28ccdf7': { zh: '协调智能体' }, + 'auto.components.settings.orchestration.search.741dfc03fa': { zh: '工作进程' }, + 'auto.components.settings.orchestration.search.eee028ae14': { zh: '调度' }, + 'auto.components.settings.orchestration.search.f5d39af41e': { zh: '子智能体' }, + 'auto.components.settings.privacy.search.2b5a5c312f': { zh: 'PostHog' }, + 'auto.components.settings.privacy.search.79c319948b': { zh: '用量' }, + 'auto.components.settings.privacy.search.b021b9cb81': { zh: '匿名' }, + 'auto.components.settings.quick.commands.search.236d4cfac8': { zh: '快捷' }, + 'auto.components.settings.quick.commands.search.8bf43c2dad': { zh: '全局' }, + 'auto.components.settings.quick.commands.search.b86c727100': { zh: 'npm' }, + 'auto.components.settings.quick.commands.search.b949a7c0a0': { zh: 'pnpm' }, + 'auto.components.settings.quick.commands.search.d07d130849': { zh: '快捷键' }, + 'auto.components.settings.repository.search.0432d2fb7c': { zh: '本地' }, + 'auto.components.settings.repository.search.1d90a6cfbb': { zh: '两者' }, + 'auto.components.settings.repository.search.4c17787d7b': { zh: '归档' }, + 'auto.components.settings.repository.search.4f3c0230c2': { zh: '稀疏' }, + 'auto.components.settings.repository.search.603c68b68c': { zh: 'orca.yaml' }, + 'auto.components.settings.repository.search.58d8bca414': { zh: '相对' }, + 'auto.components.settings.repository.search.aa42616e3d': { zh: '检出' }, + 'auto.components.settings.repository.search.ec70364df2': { zh: '工作流' }, + 'auto.components.settings.runtime.environments.search.5cd7dca3b8': { zh: '远程' }, + 'auto.components.settings.runtime.environments.search.d760866285': { zh: '客户端' }, + 'auto.components.settings.shortcuts.search.7f1b38f59a': { zh: 'TUI' }, + 'auto.components.settings.shortcuts.search.ca6a0c2df7': { zh: '快捷键' }, + 'auto.components.settings.shortcuts.search.f1adebbe8c': { zh: 'Shell' }, + 'auto.components.settings.ssh.search.237b391f7c': { zh: '连接' }, + 'auto.components.settings.ssh.search.d41f296f64': { zh: 'ping' }, + 'auto.components.settings.ssh.search.d4bcd497c7': { zh: '远程' }, + 'auto.components.settings.ssh.search.00d1fda01a': { zh: '新建' }, + 'auto.components.settings.tasks.search.604d8e4089': { zh: 'Atlassian' }, + 'auto.components.settings.tasks.search.44083ae418': { zh: '显示' }, + 'auto.components.settings.terminal.clipboard.search.d106f44fb4': { zh: '远程' }, + 'auto.components.settings.terminal.clipboard.search.e87c6d776d': { zh: '自动' }, + 'auto.components.settings.terminal.clipboard.search.5ffcd13c90': { zh: 'tmux' }, + 'auto.components.settings.terminal.search.0838b3717b': { zh: '窗口' }, + 'auto.components.settings.terminal.search.11fd3fbcf2': { zh: 'ANSI' }, + 'auto.components.settings.terminal.search.18ce996647': { zh: '垂直' }, + 'auto.components.settings.terminal.search.20ce287cc6': { zh: '字重' }, + 'auto.components.settings.terminal.search.25f606d9e5': { zh: '闪烁' }, + 'auto.components.settings.terminal.search.54a9b3725b': { zh: '水平' }, + 'auto.components.settings.terminal.search.6c2f9f05c8': { zh: '毛玻璃' }, + 'auto.components.settings.terminal.search.6cddc858ba': { zh: 'WebGL' }, + 'auto.components.settings.terminal.search.7db59c4738': { zh: 'alpha' }, + 'auto.components.settings.terminal.search.88561b3499': { zh: '冻结' }, + 'auto.components.settings.terminal.search.9f2dda133c': { zh: 'PTY' }, + 'auto.components.settings.terminal.search.a979df0083': { zh: '从 Ghostty 导入' }, + 'auto.components.settings.terminal.search.b3b94cfcb5': { zh: '国际' }, + 'auto.components.settings.terminal.search.c4427dc5ff': { zh: 'Alt' }, + 'auto.components.settings.terminal.search.fd6c24313d': { zh: '新建' }, + 'auto.components.settings.terminal.search.fae142a354': { zh: 'readline' }, + 'auto.components.settings.terminal.windows.search.28ff08ed35': { zh: 'Windows' }, + 'auto.components.settings.terminal.windows.search.1f402b3651': { zh: 'WSL 发行版' }, + 'auto.components.settings.terminal.windows.search.2b4a340ce0': { zh: '发行版' }, + 'auto.components.settings.terminal.windows.search.4ee2579c32': { zh: 'Ubuntu' }, + 'auto.components.settings.terminal.windows.search.591912177b': { zh: 'Git Bash' }, + 'auto.components.settings.terminal.windows.search.6cd20b9e64': { zh: 'cmd' }, + 'auto.components.settings.terminal.windows.search.fc564eadaf': { zh: 'Debian' }, + 'auto.components.settings.voice.pane.search.10d45a9fce': { zh: 'STT' }, + 'auto.components.settings.voice.pane.search.3d8b853963': { zh: '语音' }, + 'auto.components.settings.voice.pane.search.064a9bd94a': { zh: '按住' } +} diff --git a/config/scripts/locale-zh-manual-settings-cross-surface-key-overrides.mjs b/config/scripts/locale-zh-manual-settings-cross-surface-key-overrides.mjs new file mode 100644 index 000000000000..60092b815727 --- /dev/null +++ b/config/scripts/locale-zh-manual-settings-cross-surface-key-overrides.mjs @@ -0,0 +1,405 @@ +// Human-reviewed Simplified Chinese found during cross-surface settings review. +export const ZH_MANUAL_SETTINGS_CROSS_SURFACE_KEY_OVERRIDES = { + // Second-pass settings copy review. + 'auto.components.onboarding.AgentFeatureSetupStep.97dcdc010f': { zh: '启用功能' }, + 'auto.components.settings.browser.use.search.22fb801af8': { zh: 'Chrome 配置文件' }, + 'auto.components.settings.BrowserLocalhostWorktreeLabelsSetting.8ac8c3ad19': { + zh: 'localhost 工作树标签' + }, + 'auto.components.status.bar.UpdateStatusSegment.962404f68e': { + zh: '更新已可安装。点击展开。' + }, + 'auto.components.status.bar.UpdateStatusSegment.fd1d3b3a1d': { + zh: '正在下载更新,已完成 {{value0}}%。点击展开。' + }, + 'auto.components.settings.AdvancedNetworkSettingsSection.823e0f15b1': { + zh: '用于 Orca 网络请求和本地终端子进程的代理 URL。' + }, + 'auto.components.settings.DeveloperPermissionsPane.6f011b9bf6': { + zh: '终端工具会继承 Orca 的 macOS 隐私权限范围。' + }, + 'auto.components.settings.ExperimentalPane.a20d5ea365': { + zh: '终端响铃或智能体完成任务后,对应窗格会持续高亮,直到你与该窗格交互。此功能仍为实验性,我们正在调整提醒效果。' + }, + 'auto.components.settings.OrchestrationPane.52e0634e2c': { + zh: '让协调智能体通过编排完成任务交接、工作树交接,并调度串行或并行子智能体。' + }, + 'auto.components.settings.OrchestrationPane.2aacdb0517': { + zh: '协调编码智能体之间的任务交接、工作树交接和子智能体工作。' + }, + 'auto.components.settings.RecentTabOrderControl.7a546f2309': { zh: '标签页顺序' }, + 'auto.components.settings.RecentTabOrderControl.3b17c81ede': { zh: '标签页栏顺序' }, + 'auto.components.settings.RecentTabOrderControl.a867a0889f': { + zh: '按最近使用顺序或标签页栏顺序。' + }, + 'auto.components.settings.general.search.ca812803ea': { zh: '最近使用的标签页顺序' }, + 'auto.components.settings.floating.workspace.search.6f183fa1b9': { zh: '浮动终端' }, + 'auto.components.settings.TerminalPane.267d020745': { + zh: '滚动历史记录、单词边界和平台特定的终端行为。' + }, + 'auto.components.settings.TerminalPane.scrollSpeed.description': { + zh: '调整普通滚动历史、按修饰键时的快速滚动速度,以及全屏 TUI 的滚轮速度。' + }, + 'auto.components.settings.BaseRefPicker.a5c16712c1': { + zh: '检测到多个远程仓库。输入远程仓库名称(例如' + }, + 'auto.components.settings.BaseRefPicker.80f7c82303': { + zh: '),或输入完整引用(例如' + }, + 'auto.components.settings.BaseRefPicker.ee110e1830': { zh: '未设置默认基准引用' }, + 'auto.components.settings.GitPane.d2eede4c54': { + zh: '在提交、PR 和议题中添加 Orca 署名。' + }, + 'auto.components.settings.GitPane.976afc6b3e': { + zh: '创建工作区时,Orca 会刷新远程基准分支,并安全地快进对应的本地分支,例如' + }, + 'auto.components.settings.GitPane.db3a127eb1': { zh: '。这样,' }, + 'auto.components.settings.GitPane.36e3de3619': { + zh: '等命令就不会与过时的历史记录比较。如果该分支存在未提交的更改或仅存于本地的提交,Orca 会跳过更新。' + }, + 'auto.components.settings.GitPane.compareAgainstUpstreamDescription': { + zh: '选择源代码管理在比较已提交更改时默认采用的基准。“分支上游”会自动跟随当前分支;如果没有上游,则回退到仓库默认分支。你仍可在每个工作树的 Git 面板中单独更改比较基准。拉取请求和变基目标不受影响。' + }, + 'auto.components.settings.AutoRenameBranchFromWorkSetting.a869d0edd8': { + zh: '分支名称命令模板' + }, + 'auto.components.settings.CommitMessageAiPane.2339a89104': { + zh: '添加 AI 按钮,使用对应操作的命令模板运行所选智能体。' + }, + 'auto.components.settings.SourceControlAiActionRecipeDefaults.fb09da4345': { + zh: '命令模板' + }, + 'auto.components.settings.SourceControlAiActionRecipeDefaults.cf01d41bce': { + zh: '每个源代码管理 AI 按钮使用的智能体、CLI 参数和命令模板。' + }, + 'auto.components.settings.RepositoryHooksSection.ff082fe7c6': { zh: '工作树钩子' }, + 'auto.components.settings.RepositoryHooksSection.29397e8bbc': { + zh: '仅运行仓库中已提交的命令;忽略本地命令。' + }, + 'auto.components.settings.source.control.action.recipe.options.commitMessage': { + zh: '根据已暂存的更改生成提交信息。' + }, + 'auto.components.settings.source.control.action.recipe.options.fixCommitFailure': { + zh: '提交钩子或 git commit 失败时启动智能体。' + }, + 'auto.components.settings.PrivacyDiagnosticBundleControls.798b6f0be5': { + zh: '打开审阅文件' + }, + 'auto.components.settings.PrivacyDiagnosticBundleControls.fd7b3891af': { + zh: '你已打开审阅文件({{value0}})。可以将该文件发送给支持团队,也可以丢弃。' + }, + 'auto.components.settings.PrivacyDiagnosticBundleControls.62340d4439': { + zh: '审阅文件已准备好({{value0}})。请先打开文件查看将要发送的内容,再决定是否发送给支持团队。' + }, + 'auto.components.settings.PrivacyDiagnosticBundleControls.19ec5e29b3': { + zh: '将最近的应用活动和错误汇总到一份已脱敏文件中,发送前可供你审阅。在你选择发送之前,不会上传任何内容。' + }, + 'auto.components.settings.cli.source.control.integration.cards.b4d900e7f1': { + zh: '拉取请求、议题和检查由' + }, + 'auto.components.settings.cli.source.control.integration.cards.1f2b347bd3': { + zh: '合并请求、议题、待办事项和流水线由' + }, + 'auto.components.settings.cli.source.control.integration.cards.a47f71e357': { + zh: 'CLI 提供。' + }, + + // Cross-surface terminology and accessibility copy found during final review. + 'auto.components.settings.CommitMessageAiPane.841ed9884a': { + zh: '由未自定义源代码管理 AI 的仓库使用。' + }, + 'auto.components.settings.CommitMessageAiPane.ad66ff886d': { zh: '源代码管理 AI 默认值' }, + 'auto.components.settings.CommitMessageAiPane.d5b45a3628': { + zh: '显示源代码管理 AI 操作' + }, + 'auto.components.settings.RepositorySourceControlAiEnablement.cf5959c834': { + zh: '启用源代码管理 AI' + }, + 'auto.components.settings.RepositorySourceControlAiEnablement.showActionsLabel': { + zh: '显示源代码管理 AI 操作' + }, + 'auto.components.settings.RepositorySourceControlAiEnablement.visibilityHelper': { + zh: '控制是否为此仓库显示源代码管理 AI 按钮。各项独立功能仍遵循自身的生成设置。全局默认值为 {{value0}}。' + }, + 'auto.components.settings.RepositorySourceControlAiSection.71b003b62b': { + zh: '源代码管理 AI' + }, + 'auto.components.settings.Settings.70100f94c7': { zh: 'Git 与源代码管理' }, + 'auto.components.settings.SourceControlAiActionRecipeDefaults.b5f46664d3': { + zh: '无法保存源代码管理 AI 操作默认值:{{value0}}' + }, + 'auto.components.settings.commit.message.ai.search.82109d627d': { zh: '源代码管理' }, + 'auto.components.settings.commit.message.ai.search.24dbdfca78': { + zh: '显示源代码管理 AI 操作' + }, + 'auto.components.settings.repository.search.a47f51127e': { zh: '源代码管理' }, + 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.d7f80060ca': { zh: '源代码管理' }, + 'auto.components.settings.DeveloperPermissionsPane.7ca17b62c8': { + zh: '当项目、工作树或符号链接文件涉及受 macOS 保护的文件夹时,建议启用。' + }, + 'auto.components.settings.ExperimentalPane.newWorktreeCardStyle.copy': { + zh: '预览新版工作树卡片布局、元数据位置、卡片显示菜单选项和状态展示。' + }, + 'auto.components.settings.ExperimentalPane.newWorktreeCardStyle.description': { + zh: '预览新版工作树卡片布局、元数据位置、卡片显示菜单选项和状态展示。' + }, + 'auto.components.settings.developer.permissions.search.05ab708ee5': { + zh: '打开 macOS 隐私设置,以允许访问受保护文件夹中的项目和工作树文件。' + }, + 'auto.components.settings.experimental.search.newWorktreeCardStyle.description': { + zh: '预览新版工作树卡片布局、元数据位置、卡片显示菜单选项和状态展示。' + }, + 'auto.components.settings.git.search.compareAgainstUpstreamDescription': { + zh: '选择源代码管理在比较已提交更改时默认采用的基准。“分支上游”会自动跟随当前分支;如果没有上游,则回退到仓库默认分支。你仍可在每个工作树的 Git 面板中单独更改比较基准。拉取请求和变基目标不受影响。' + }, + 'auto.components.feature.wall.FullDiskAccessSetupPrompt.0d6efe9cf4': { + zh: '项目或工作树位于受保护文件夹时,建议在 macOS 上启用。' + }, + 'auto.components.settings.RepositoryIconPicker.4e2a14f967': { zh: '仓库图标' }, + 'auto.components.settings.RepositoryIconPicker.2b7d27b93c': { + zh: '使用 {{value0}} 仓库颜色' + }, + 'auto.components.settings.RepositoryIconPicker.useRepoIcon': { + zh: '使用 {{value0}} 仓库图标' + }, + 'auto.components.settings.RepositorySourceControlAiCustomCommand.fbb77e122a': { + zh: '选择自定义命令的文本操作所使用的仓库级回退方案。' + }, + 'auto.components.settings.general.search.b9cffd374d': { + zh: '在以仓库命名的子文件夹内创建工作区。' + }, + 'auto.components.settings.integrations.search.ed63380247': { zh: 'Azure Repos' }, + 'auto.components.settings.integrations.search.b38b5d27f1': { zh: 'Azure DevOps' }, + 'auto.components.settings.git.search.118c23484b': { + zh: '在提交、PR 和议题中添加 Orca 署名。' + }, + 'auto.components.settings.git.search.0e993bf00f': { + zh: '创建工作区时,Orca 会刷新远程基准,并将对应的本地分支(如 main 或 master)安全地快进到最新状态。这样,git diff main...HEAD 等命令就不会再与过时的历史记录比较。若该分支有未提交的更改或仅存在于本地的提交,Orca 会跳过更新。' + }, + 'auto.components.settings.token.source.control.integration.cards.05863d2599': { + zh: '通过 Gitea REST API 获取拉取请求和提交状态。' + }, + 'auto.components.settings.token.source.control.integration.cards.52f75876be': { + zh: '已检测仓库的拉取请求和提交状态' + }, + 'auto.components.settings.token.source.control.integration.cards.0eb50d5593': { + zh: '通过 Azure DevOps REST API token 获取拉取请求和构建状态。' + }, + 'auto.components.settings.token.source.control.integration.cards.54636c65d4': { + zh: '已检测 Azure Repos 的拉取请求和构建状态' + }, + 'auto.components.settings.token.source.control.integration.cards.a924e8dcd1': { + zh: '通过 Bitbucket Cloud API token 获取拉取请求和构建状态。' + }, + 'auto.components.settings.token.source.control.integration.cards.0fa5629dad': { + zh: '拉取请求和构建状态' + }, + 'auto.components.settings.ManageSessionKillDialog.d3dba51b15': { zh: '正在强制结束…' }, + 'auto.components.settings.GeneralEditorSettingsSection.05b6df93b3': { zh: '行内' }, + 'auto.components.settings.general.search.256d92554d': { zh: '标签页顺序' }, + 'auto.components.settings.repository.search.eec39b3de6': { zh: '提交信息' }, + 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.ab2901bce6': { zh: '检查' }, + 'auto.components.settings.Settings.f75daf1002': { zh: '移动端模拟器' }, + 'auto.components.settings.mobile.emulator.search.cdd3c31918': { zh: '移动端模拟器' }, + 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.2f37142229': { + zh: '压缩并合并' + }, + 'auto.components.settings.experimental.search.9e4ddf776d': { zh: '终端提醒' }, + 'auto.components.settings.general.search.d2d2d929c0': { + zh: '富文本 Markdown 拼写检查' + }, + 'auto.components.settings.RepositoryPane.f88db4fece': { zh: '默认工作树基准' }, + 'auto.components.settings.repository.search.094adbe930': { zh: '默认工作树基准' }, + 'auto.components.settings.terminal.search.2d5ab88b7c': { zh: '分隔线粗细' }, + 'auto.components.settings.terminal.search.7f1e356a54': { zh: '光标不透明度' }, + 'auto.components.settings.terminal.search.b03d01fd49': { zh: '闪烁光标' }, + 'auto.components.settings.terminal.search.97bcfff662': { zh: '光标形状' }, + 'auto.components.settings.terminal.search.e989914ad6': { zh: '字体系列' }, + 'auto.components.settings.terminal.search.9bd7229927': { zh: '将 Option 用作 Alt' }, + 'auto.components.settings.terminal.search.7674e758e1': { zh: '终端历史记录行数' }, + 'auto.components.settings.TerminalPane.d23b43c5be': { zh: '设置脚本运行位置' }, + 'auto.components.settings.terminal.search.5be2d67678': { zh: '设置脚本运行位置' }, + 'auto.components.settings.TerminalThemeSections.ec2e33ad80': { + zh: '浅色模式分隔线颜色' + }, + 'auto.components.settings.terminal.search.595b97b446': { zh: '浅色模式分隔线颜色' }, + 'auto.components.settings.TerminalWindowSection.36b8402015': { zh: '水平内边距' }, + 'auto.components.settings.terminal.search.b4f182f24d': { zh: '水平内边距' }, + 'auto.components.settings.browser.search.19ea5607cf': { zh: 'localhost 工作树标签' }, + 'auto.components.settings.browser.search.4e0fdf0a3f': { + zh: '将工作区端口作为工作树专属的 Orca localhost URL 打开,便于区分浏览器标签页。' + }, + 'auto.components.settings.BrowserLocalhostWorktreeLabelsSetting.1db3c8b983': { + zh: '将工作区端口作为工作树专属的 Orca localhost URL 打开,便于区分浏览器标签页。' + }, + 'auto.components.settings.browser.search.5cb082b3e3': { zh: '链接路由' }, + 'auto.components.settings.appearance.search.ddb991024d': { zh: 'IDE 字体' }, + 'auto.components.settings.appearance.search.fdd31b00d0': { zh: '标题栏应用名称' }, + 'auto.components.settings.git.search.de06e9d105': { zh: '基础引用' }, + 'auto.components.settings.repository.search.f41cef5083': { zh: '基础引用' }, + 'auto.components.settings.RepositoryPane.8984d06520': { + zh: '创建工作树时使用的默认基础分支或引用。' + }, + 'auto.components.settings.repository.search.f571081ec4': { + zh: '创建工作树时使用的默认基础分支或引用。' + }, + 'auto.components.settings.IntegrationsPane.1a62c295c6': { + zh: 'Gitea 凭据已配置,但身份验证失败。请检查 token、API 基础 URL 和仓库权限;若环境变量已更改,请重启 Orca。' + }, + 'auto.components.settings.IntegrationsPane.953b7bf6f7': { + zh: 'Azure DevOps 凭据已配置,但身份验证失败。请检查 token、API 基础 URL 和仓库权限;若环境变量已更改,请重启 Orca。' + }, + 'auto.components.settings.IntegrationsPane.6f317f5132': { + zh: '仅当 Orca 无法从 Git 远程地址推导出 API 基础 URL 时。' + }, + 'auto.components.settings.RepositoryHooksSection.30952c4aa4': { + zh: '主仓库检出目录的路径,可用于将 .env 等共享文件复制到工作树。' + }, + 'auto.components.settings.RepositoryHooksSection.f0710e1c83': { + zh: '创建新工作树后运行;可用于安装依赖、复制环境配置文件和运行迁移。' + }, + 'auto.components.settings.token.source.control.integration.cards.60708f23da': { + zh: '仅当 Orca 无法从远程仓库推导出 API URL 时。' + }, + 'auto.components.settings.token.source.control.integration.cards.fcbe0469fd': { + zh: '公开仓库会根据 Git 远程仓库自动检测。设置' + }, + 'auto.components.settings.token.source.control.integration.cards.7bd345e3f6': { + zh: '仅当 Orca 无法从 Git 远程仓库推导出 API 基础 URL 时。' + }, + 'auto.components.settings.TerminalPane.a016ffbeed': { + zh: '自动模式目前使用 Windows PowerShell;安装 PowerShell 7+ 后会自动切换。' + }, + 'auto.components.settings.TerminalPane.e0996d141a': { + zh: '自动模式会尝试使用 WebGL;若渲染器不受支持或存在风险,则回退到 DOM。' + }, + 'auto.components.settings.TerminalPane.f07dfb4466': { + zh: '控制终端是否使用 xterm.js 的 WebGL 渲染。渲染器受支持时,自动模式会尝试 WebGL;在 Linux 上遇到软件渲染器或未知 GPU 时,则采用保守的回退方案。' + }, + 'auto.components.settings.RuntimePairingUrlGenerator.279e0dcb57': { + zh: '127.0.0.1 只能在此电脑上使用。如需连接其他设备,请改用 LAN、Tailscale 或自定义地址。' + }, + 'auto.components.emulator.pane.MobileEmulatorAgentSetupGuideSteps.64fb057667': { + zh: '向智能体介绍此工作树可用的 orca emulator 命令。' + }, + 'auto.components.settings.CliSection.6053cf736c': { zh: 'CLI 技能' }, + 'auto.components.settings.CliSkillRuntimeSetup.3728a94fb6': { + zh: 'WSL shell 命令需要处理' + }, + 'auto.components.settings.GhosttyImportModal.d2f33670a9': { zh: '从 Ghostty 导入' }, + 'auto.components.settings.GitPane.0de4ae556c': { zh: 'GitLab API 预算' }, + 'auto.components.settings.GitPane.cdd793134e': { zh: 'API 预算' }, + 'auto.components.settings.GitPane.a182c5125e': { zh: 'Git 用户名' }, + 'auto.components.settings.InputPane.db15068196': { + zh: '在 Linux 和 macOS 上默认启用。Linux 使用系统选择剪贴板;其他平台使用私有缓冲区。' + }, + 'auto.components.settings.input.search.874d88f4a6': { + zh: '在 Linux 和 macOS 上默认启用。Linux 使用系统选择剪贴板;其他平台使用私有缓冲区。' + }, + 'auto.components.settings.OpenAiTranscriptionKeyDialog.16015322f9': { zh: 'API 密钥' }, + 'auto.components.settings.OpenAiTranscriptionSettingsRow.85c589cd61': { + zh: '添加 API 密钥' + }, + 'auto.components.settings.integrations.search.faa0b5a0d9': { zh: 'API 密钥' }, + 'auto.components.settings.voice.pane.search.2d206de105': { zh: 'API 密钥' }, + 'auto.components.settings.RepositoryIconPicker.381b4844fd': { zh: '上传 PNG' }, + 'auto.components.settings.git.search.40f9b815fd': { zh: 'API 预算' }, + 'auto.components.settings.git.search.83ecb3f470': { zh: 'GitLab API 预算' }, + 'auto.components.settings.terminal.clipboard.search.62d1208b90': { zh: 'OSC 52' }, + 'auto.components.settings.terminal.search.13a2502dfc': { zh: 'GPU 加速' }, + 'auto.components.settings.TerminalWindowSection.25e2f8e8e1': { + zh: '终端网格周围的水平内边距(以像素为单位)。' + }, + 'auto.components.settings.terminal.search.75691e4911': { + zh: '终端网格周围的水平内边距(以像素为单位)。' + }, + 'auto.components.settings.AgentsPane.d4d2a45d63': { zh: '已启用' }, + 'auto.components.settings.BrowserUsePane.0289434ed6': { zh: '已启用' }, + 'auto.components.settings.PrivacyPane.79a0f3c16c': { zh: '由于设置了' }, + 'auto.components.settings.PrivacyPane.36e0e2e63b': { + zh: '环境变量,遥测已被禁用。请取消设置该变量并重启,以重新启用遥测。' + }, + 'auto.components.settings.NotificationsPane.115437bc35': { + zh: '如果没有出现 macOS 通知横幅,请在系统设置中为 Orca 开启“允许通知”。' + }, + 'auto.components.settings.MobileEmulatorAgentControlRow.d94ca6a623': { + zh: '让智能体使用 Orca CLI 命令,包括控制移动端模拟器。' + }, + 'auto.components.settings.MobileEmulatorAgentControlRow.ff4b7e65d6': { + zh: '让智能体通过 Orca CLI 命令控制当前移动端模拟器。' + }, + 'auto.components.settings.MobileEmulatorAgentControlRow.2a674aa810': { + zh: '智能体控制移动端模拟器' + }, + 'auto.components.settings.MobileEmulatorSettingsPane.19d39113b6': { + zh: '让智能体通过 Orca CLI 命令控制当前移动端模拟器。' + }, + 'auto.components.settings.MobileEmulatorSettingsPane.f2f8d97bb6': { + zh: '智能体控制移动端模拟器' + }, + 'auto.components.settings.MobileEmulatorSettingsPane.f9af91ea26': { + zh: '显示“新建移动端模拟器”操作,并允许智能体连接当前模拟器。' + }, + 'auto.components.settings.MobileEmulatorSettingsPane.bc39d0f115': { + zh: '为 Orca 和智能体配置移动端模拟器支持。' + }, + 'auto.components.settings.Settings.01f9d36292': { + zh: '为 Orca 和智能体配置移动端模拟器支持。' + }, + 'auto.components.settings.mobile.emulator.search.2e0b45b2ba': { + zh: '使用 Orca CLI 命令列出、连接、点击移动端模拟器并向其中输入内容。' + }, + 'auto.components.settings.mobile.emulator.search.9595354cff': { + zh: '为 Orca 和智能体配置移动端模拟器支持。' + }, + 'auto.components.settings.RepositoryHooksSection.4611b78617': { zh: '命令来源' }, + 'auto.components.settings.repository.search.ed269fad69': { zh: '命令来源' }, + 'auto.components.settings.RepositorySourceControlAiHostedReviewDefaults.14f1eb99d0': { + zh: '打开“创建 PR”时生成详情' + }, + 'auto.components.settings.WarpThemeImportModal.custom_theme_yaml_hint': { + zh: '自定义和社区主题必须先以 YAML 文件形式放入 Warp 主题文件夹,自动导入才能找到它们。如果你克隆了 Warp 的公共主题仓库,请使用“选择文件夹”导入该检出目录。' + }, + 'auto.components.settings.IntegrationsPane.3c3cf05c63': { + zh: 'Bitbucket 凭据已配置,但身份验证失败。请检查 token 和仓库权限;若环境变量已更改,请重启 Orca。' + }, + 'auto.components.settings.RepositoryHooksSection.ecc73d9125': { + zh: '请将你的文件与下方模板对照,并按需复制其结构。' + }, + 'auto.components.settings.RepositoryHooksSection.54c73d88d0': { + zh: '正在创建的工作树路径。初始化命令会从此目录运行。' + }, + 'auto.components.settings.RepositoryHooksSection.52aef29e69': { + zh: '留空则使用仓库默认值,来源为' + }, + 'auto.components.settings.RepositoryHooksSection.9b12f15b1e': { + zh: '(如果该文件存在)。' + }, + 'auto.components.settings.terminal.search.893aa92997': { + zh: '为自带编程连字(例如 =>、→、≠、≥)的字体启用连字渲染。“自动”仅对已知支持连字的字体(Fira Code、JetBrains Mono、Cascadia Code、Iosevka 等)启用。' + }, + 'auto.components.settings.VoicePane.f9a9cf6928': { + zh: '启用语音听写前,需要授予麦克风权限。' + }, + 'auto.components.settings.FloatingWorkspacePane.37df688d6f': { + zh: '启用浮动工作区,并选择新标签页的打开位置。' + }, + 'auto.components.settings.floating.workspace.search.b96b5ee6cf': { + zh: '启用浮动工作区,选择新标签页的打开位置,以及切换按钮的显示位置。' + }, + 'auto.components.settings.GeneralWorkspaceSettingsSection.28bc3d085e': { + zh: '从上下文菜单删除工作区前显示确认。删除失败时仍会提供“强制删除”选项。' + }, + 'auto.components.settings.TerminalPane.fe4acf36c6': { + zh: 'WebGL 已禁用;改用 DOM 渲染器以获得最佳兼容性。' + }, + 'auto.components.settings.TerminalPane.16753eea48': { + zh: '右键点击会粘贴剪贴板内容。Ctrl+右键点击会打开上下文菜单。' + }, + 'auto.components.settings.TerminalPane.af0c3b6e39': { + zh: '右键点击会将剪贴板内容粘贴到终端。使用 Ctrl+右键点击可打开上下文菜单。' + }, + 'auto.components.settings.AppearancePane.leftSidebarAppearance.tintOpacityDescription': { + zh: '控制色调混入侧边栏的强度。' + } +} diff --git a/config/scripts/locale-zh-manual-settings-key-overrides.mjs b/config/scripts/locale-zh-manual-settings-key-overrides.mjs new file mode 100644 index 000000000000..2c643705a663 --- /dev/null +++ b/config/scripts/locale-zh-manual-settings-key-overrides.mjs @@ -0,0 +1,10 @@ +import { ZH_MANUAL_SETTINGS_CORE_KEY_OVERRIDES } from './locale-zh-manual-settings-core-key-overrides.mjs' +import { ZH_MANUAL_SETTINGS_CROSS_SURFACE_KEY_OVERRIDES } from './locale-zh-manual-settings-cross-surface-key-overrides.mjs' +import { ZH_MANUAL_SETTINGS_ONBOARDING_MOBILE_KEY_OVERRIDES } from './locale-zh-manual-settings-onboarding-mobile-key-overrides.mjs' + +// Keep the public settings override export stable while domain modules stay below lint limits. +export const ZH_MANUAL_SETTINGS_KEY_OVERRIDES = { + ...ZH_MANUAL_SETTINGS_CORE_KEY_OVERRIDES, + ...ZH_MANUAL_SETTINGS_ONBOARDING_MOBILE_KEY_OVERRIDES, + ...ZH_MANUAL_SETTINGS_CROSS_SURFACE_KEY_OVERRIDES +} diff --git a/config/scripts/locale-zh-manual-settings-onboarding-mobile-key-overrides.mjs b/config/scripts/locale-zh-manual-settings-onboarding-mobile-key-overrides.mjs new file mode 100644 index 000000000000..79d24e972dbc --- /dev/null +++ b/config/scripts/locale-zh-manual-settings-onboarding-mobile-key-overrides.mjs @@ -0,0 +1,345 @@ +// Human-reviewed Simplified Chinese for onboarding, feature tours, and mobile setup. +export const ZH_MANUAL_SETTINGS_ONBOARDING_MOBILE_KEY_OVERRIDES = { + // Onboarding + 'auto.components.onboarding.AgentStep.1eee1c7bd8': { + zh: 'PATH 中未检测到智能体。可选择一个稍后安装,或使用空白终端继续。' + }, + 'auto.components.onboarding.AgentStep.d7b3ef168b': { zh: '系统已检测到' }, + 'auto.components.onboarding.AgentStep.e6a369bd04': { zh: '热门智能体' }, + 'auto.components.onboarding.AgentStep.hideAgents': { zh: '隐藏智能体' }, + 'auto.components.onboarding.AgentStep.showMoreAgents': { zh: '再显示 {{value0}} 个智能体 →' }, + 'auto.components.onboarding.AgentStep.yoloPermissionsLabel': { + zh: 'Yolo / 跳过权限检查(有风险)' + }, + 'auto.components.onboarding.AgentStep.yoloPermissionsTooltip': { + zh: '跳过智能体的权限检查,减少权限提示' + }, + 'auto.components.onboarding.FeatureSetupChecklist.ea85d9e628': { zh: '智能体浏览器操控' }, + 'auto.components.onboarding.FeatureSetupChecklist.linearTicketsDescription': { + zh: '智能体可使用已链接的 Linear 任务,让交接包含更完整的任务背景。' + }, + 'auto.components.onboarding.IntegrationsStep.0b4a7d23ab': { zh: '正在登录' }, + 'auto.components.onboarding.IntegrationsStep.bd5d976fb2': { zh: '安装 gh' }, + 'auto.components.onboarding.IntegrationsStep.c1547656f0': { zh: '正在检查…' }, + 'auto.components.onboarding.NotificationStep.0fe570690c': { + zh: '选择 Orca 在送达桌面通知后播放的提示音。' + }, + 'auto.components.onboarding.NotificationStep.2c47f5465f': { + zh: '打开“系统设置”,为 Orca 启用“允许通知”。启用后,此步骤会自动更新。' + }, + 'auto.components.onboarding.NotificationStep.8124d085a6': { zh: '打开 macOS 设置' }, + 'auto.components.onboarding.NotificationStep.94562ba367': { + zh: 'macOS 正在请求权限。请在对话框中点击“允许”,此步骤会自动更新。' + }, + 'auto.components.onboarding.NotificationStep.e52aacf380': { zh: '正在加载通知设置…' }, + 'auto.components.onboarding.OnboardingFlow.windowsTerminalSubtitle': { + zh: '选择新窗格的默认 Shell,并设置终端中的右键行为。' + }, + 'auto.components.onboarding.OnboardingFlow.windowsTerminalTitle': { + zh: '设置 Windows 终端默认值' + }, + 'auto.components.onboarding.OnboardingInlineCommandTerminal.4123609efd': { zh: '正在启动终端…' }, + 'auto.components.onboarding.RepoStep.132425a3e3': { zh: '克隆仓库' }, + 'auto.components.onboarding.RepoStep.24c7c8696c': { zh: '克隆到主机路径' }, + 'auto.components.onboarding.RepoStep.2d20200346': { zh: '导入仓库' }, + 'auto.components.onboarding.RepoStep.466108ab89': { zh: '输入所选主机上已存在的路径。' }, + 'auto.components.onboarding.RepoStep.6558d50c69': { + zh: '想一次导入多个仓库?请选择它们的父文件夹。' + }, + 'auto.components.onboarding.RepoStep.831524961f': { + zh: '选择任意本地目录,无论它是否为 Git 仓库。' + }, + 'auto.components.onboarding.RepoStep.8cab104e3c': { zh: '打开此主机上的项目' }, + 'auto.components.onboarding.RepoStep.c33b190ca3': { zh: '仅限主机路径' }, + 'auto.components.onboarding.RepoStep.cf23006ba7': { zh: '所选主机' }, + 'auto.components.onboarding.RepoStep.e8fdb36338': { zh: '正在扫描仓库。点击可停止。' }, + 'auto.components.onboarding.ThemeStep.94b9dc561d': { zh: '设置 → 终端' }, + 'auto.components.onboarding.ThemeStep.ab2a583a97': { zh: '光标' }, + 'auto.components.onboarding.ThemeStep.dd5c16ad1b': { + zh: '更多终端选项(包括字体、光标和配色)位于' + }, + 'auto.components.onboarding.WindowsTerminalStep.commandPromptDescription': { + zh: '使用经典 cmd.exe 行为打开新的终端窗格。' + }, + 'auto.components.onboarding.WindowsTerminalStep.defaultShellDescription': { + zh: '选择 Orca 为新的 Windows 终端窗格打开的 Shell。' + }, + 'auto.components.onboarding.WindowsTerminalStep.gitBash': { zh: 'Git Bash' }, + 'auto.components.onboarding.WindowsTerminalStep.loading': { zh: '正在加载终端设置…' }, + 'auto.components.onboarding.WindowsTerminalStep.rightClickBehaviorDescription': { + zh: '选择符合你 Windows 使用习惯的终端鼠标行为。' + }, + 'auto.components.onboarding.WindowsTerminalStep.rightClickMenuDescription': { + zh: '右键点击会打开终端菜单。可从菜单或使用键盘粘贴。' + }, + 'auto.components.onboarding.WindowsTerminalStep.wslDescription': { + zh: '在默认的 Windows Subsystem for Linux 环境中启动新的终端窗格。' + }, + 'auto.components.onboarding.WindowsTerminalStep.wslDistributionDescription': { + zh: '使用 WSL 默认发行版,或选择特定的已安装发行版。' + }, + + // Feature wall and guided tour + 'auto.components.feature.wall.AgentCapabilitiesSetupAction.1b51644c2d': { + zh: '让智能体控制桌面:移动光标、点击,并在任意应用中输入。' + }, + 'auto.components.feature.wall.AgentCapabilitiesSetupAction.e9eb197e12': { + zh: '已打开“计算机控制”权限设置' + }, + 'auto.components.feature.wall.AiCommitPrSettingsCard.4b2fc4b80c': { zh: '推理强度' }, + 'auto.components.feature.wall.AiCommitPrSettingsCard.4d9b6d84df': { + zh: '不支持。请选择 Claude、Codex 或自定义命令。' + }, + 'auto.components.feature.wall.AiCommitPrSettingsCard.8d4152701a': { + zh: '例如:ollama run llama3.1 {{value0}}' + }, + 'auto.components.feature.wall.AiCommitPrSettingsCard.f9382b48a1': { zh: '启用 AI 作者' }, + 'auto.components.feature.wall.BrowserAnimatedVisual.1bec24acc1': { + zh: '@keyframes browserFlash { 0% { opacity: 0; } 20% { opacity: 0.85; } 100% { opacity: 0; } } @keyframes browserTabIn { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } } @keyframes browserViewIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }' + }, + 'auto.components.feature.wall.BrowserAnimatedVisual.73bbb46073': { zh: '/pricing' }, + 'auto.components.feature.wall.BrowserAnimatedVisual.7da6eed7bf': { zh: 'localhost:3000' }, + 'auto.components.feature.wall.BrowserAnimatedVisual.f39be6ca14': { zh: '/signup' }, + 'auto.components.feature.wall.ComputerUseAnimatedVisual.6804cb356f': { zh: '已发送点击操作' }, + 'auto.components.feature.wall.ComputerUseAnimatedVisual.bdd5312213': { zh: '待处理' }, + 'auto.components.feature.wall.EditorAnimatedVisual.4426aab46f': { + zh: '新卡片上线后更新文档索引。' + }, + 'auto.components.feature.wall.EditorAnimatedVisual.5a55c00a81': { zh: '发布计划' }, + 'auto.components.feature.wall.EditorAnimatedVisual.e16479c1c5': { + zh: '[data-slash-menu] [data-slash-row].slash-active { background: rgba(24,24,27,0.07); box-shadow: inset 0 0 0 1px rgba(24,24,27,0.06); } [data-md-active-line][data-role="active"] { color: rgb(113 113 122); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; } [data-md-active-line][data-role="h1"] { color: inherit; font-family: inherit; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.2; margin-top: 6px; } [data-md-caret] { display: inline-block; width: 1.5px; height: 1em; background: currentColor; vertical-align: -2px; margin-left: 1px; animation: md-caret-blink 1.05s steps(1) infinite; } @keyframes md-caret-blink { 0%, 50% { opacity: 1 } 51%, 100% { opacity: 0 } } @keyframes md-block-in { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } } @keyframes md-cursor-ripple { 0% { transform: scale(0.4); opacity: 0.9; } 100% { transform: scale(1.4); opacity: 0; } } [data-clicking="1"] [data-cursor-ripple] { animation: md-cursor-ripple 460ms ease-out forwards; }' + }, + 'auto.components.feature.wall.FeatureTourPreview.1aa8a9a24a': { zh: '可拆分终端' }, + 'auto.components.feature.wall.FeatureTourPreview.24fedd5a52': { zh: 'login.spec.ts' }, + 'auto.components.feature.wall.FeatureTourPreview.304ad0dfc1': { zh: '思考中…' }, + 'auto.components.feature.wall.FeatureTourPreview.3822d8d14b': { + zh: 'fix/worktree-picker-truncates' + }, + 'auto.components.feature.wall.FeatureTourPreview.3c4adfd821': { zh: '修复登录竞态条件' }, + 'auto.components.feature.wall.FeatureTourPreview.5171768676': { zh: '正在编排 3 个智能体' }, + 'auto.components.feature.wall.FeatureTourPreview.56a0271428': { zh: '隔离工作区' }, + 'auto.components.feature.wall.FeatureTourPreview.6218a9014d': { zh: 'pnpm playwright test' }, + 'auto.components.feature.wall.FeatureTourPreview.6ed43cb0e0': { zh: 'dashboard.spec.ts' }, + 'auto.components.feature.wall.FeatureTourPreview.70aa182266': { + zh: '交付一个目标后即可离开。协调智能体会拆分工作,并行交付多个 PR。' + }, + 'auto.components.feature.wall.FeatureTourPreview.d54aefe09e': { zh: 'LIN-329' }, + 'auto.components.feature.wall.FeatureTourPreview.ef8b164dd1': { zh: '评审 src/auth' }, + 'auto.components.feature.wall.FeatureWallModal.33dca8bbbe': { zh: '按工作流快速了解 Orca。' }, + 'auto.components.feature.wall.FeatureWallPreview.a666384798': { zh: '此工作流还包括' }, + 'auto.components.feature.wall.FeatureWallRail.7593d15f94': { zh: '工作流' }, + 'auto.components.feature.wall.FeatureWallSetupWorkflowActions.486c2f4d8d': { + zh: '请先添加 Git 项目,再为该仓库配置初始化脚本。' + }, + 'auto.components.feature.wall.FeatureWallSetupWorkflowActions.5c5b65044e': { zh: 'pnpm install' }, + 'auto.components.feature.wall.ReviewNotesAnimatedVisual.294aaff104': { zh: '将评审笔记发送给' }, + 'auto.components.feature.wall.ReviewNotesAnimatedVisual.a7a89d8f94': { zh: '行' }, + 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.6f4c2d7cb7': { + zh: '添加覆盖率测试,覆盖' + }, + 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.7c2808ecff': { + zh: '在合并前被截断的情况。' + }, + 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.8ed213397c': { zh: '运行中' }, + 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.c2062da7ec': { zh: 'stderr' }, + 'auto.components.feature.wall.ReviewPRViewAnimatedVisual.dfe313e0c9': { zh: '未关闭' }, + 'auto.components.feature.wall.ReviewShipAnimatedVisual.7347fa5839': { zh: '消息' }, + 'auto.components.feature.wall.ReviewShipAnimatedVisual.a079083a6c': { zh: '提交' }, + 'auto.components.feature.wall.ReviewShipAnimatedVisual.cd8a3a39d7': { zh: '领先 3 个提交' }, + 'auto.components.feature.wall.ReviewShipAnimatedVisual.d1a7f15876': { + zh: '使用 AI 生成提交信息' + }, + 'auto.components.feature.wall.TasksAnimatedVisual.72f9e516a3': { zh: 'pressing' }, + 'auto.components.feature.wall.TasksAnimatedVisual.4331c4d0f8': { zh: '未关闭' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.0b20782e0f': { + zh: '使用 4 个工作进程运行 12 项测试' + }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.17cfdc3344': { zh: 'Grep' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.4371cc9931': { zh: 'pnpm playwright test' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.633a91e358': { zh: '思考中…' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.944199e54a': { zh: '› 购物车总额会更新' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.9923847785': { zh: '读取' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.c0eb94125e': { zh: '评审身份验证边界情况' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.ca2cfbf188': { zh: '向下拆分终端' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.defe550fe2': { zh: 'login.spec.ts' }, + 'auto.components.feature.wall.WorkbenchAnimatedVisual.e370fa8c2b': { zh: '向右拆分终端' }, + 'auto.components.feature.wall.agents.orchestration.OrchestrationPage.30b509a467': { + zh: '2 个子工作区' + }, + 'auto.components.feature.wall.agents.orchestration.StatusesPage.78f0318ac1': { zh: '请求运行' }, + 'auto.components.feature.wall.agents.orchestration.StatusesPage.7b26349cb2': { + zh: 'pnpm migrate latest' + }, + 'auto.components.feature.wall.agents.orchestration.UsageAccountsCard.29d0653961': { zh: '登录' }, + 'auto.components.feature.wall.agents.orchestration.UsageAccountsCard.6986b36708': { + zh: '直接显示速率限制,并可就地切换账户。' + }, + 'auto.components.feature.wall.agents.orchestration.UsageAccountsCard.945865332e': { + zh: '正在登录' + }, + 'auto.components.feature.wall.agents.orchestration.UsageAccountsCard.c7b90c140b': { + zh: '已添加 Codex 账户。' + }, + 'auto.components.feature.wall.agents.orchestration.UsagePage.4dce5ca3aa': { + zh: '4 天 3 小时后重置' + }, + 'auto.components.feature.wall.connect.integration.step.open_step': { zh: '打开' }, + 'auto.components.feature.wall.feature.wall.usage.tracking.cc39a87288': { + zh: '已连接 · 系统默认' + }, + + 'auto.components.settings.general.search.e3b1d42f95': { + zh: '用于 Orca 网络请求和本地终端子进程的代理 URL。' + }, + + // Mobile page and mobile settings + 'auto.components.mobile.CustomNetworkAddressDialog.hint': { + zh: '输入 IP 地址或主机名,例如 Tailscale MagicDNS 名称、DDNS 域名或 LAN 主机名;也可附加 :port。' + }, + 'auto.components.mobile.MobileHero.0d9b33299e': { zh: '下载应用。' }, + 'auto.components.mobile.MobileHero.266c18c105': { + zh: '打开 Orca Mobile,从上次离开的地方继续,或配对另一台设备。' + }, + 'auto.components.mobile.MobileHero.65b3f2e8bc': { zh: '正在生成…' }, + 'auto.components.mobile.MobileHero.79d2f480da': { zh: '要公布的网络接口' }, + 'auto.components.mobile.MobileHero.901c98bb93': { zh: '配对此' }, + 'auto.components.mobile.MobileHero.94829abdb1': { zh: '已配对' }, + 'auto.components.mobile.MobileHero.a8fb43cf1c': { zh: '继续' }, + 'auto.components.mobile.MobileHero.ca85e595a7': { zh: '未找到网络接口' }, + 'auto.components.mobile.MobileHero.cd4e5e816f': { zh: '工作区,随身掌握。' }, + 'auto.components.mobile.MobileHero.da1d5e5ed0': { zh: '支持平台' }, + 'auto.components.mobile.MobileHero.ec0607bf66': { zh: '支持的移动平台' }, + 'auto.components.mobile.MobilePage.1b4509a8a1': { zh: '已配对' }, + 'auto.components.mobile.MobilePageToolbar.9883b58693': { zh: '关闭 Orca Mobile' }, + 'auto.components.mobile.PhoneCarousel.96d651cb87': { zh: '终端会话' }, + 'auto.components.mobile.WindowsFirewallNotice.allow': { zh: '允许手机连接' }, + 'auto.components.mobile.WindowsFirewallNotice.blocked-description': { + zh: '现有的入站“阻止”规则可能会覆盖配对例外。修复操作会移除此 Orca 应用冲突的 TCP 规则,然后允许专用网络通过端口 {{port}} 连接。' + }, + 'auto.components.mobile.WindowsFirewallNotice.blocked-title': { + zh: 'Windows 可能正在阻止 Orca Mobile' + }, + 'auto.components.mobile.WindowsFirewallNotice.missing-description': { + zh: 'Windows 可能会阻止配对服务器。请为此 Orca 应用添加一条规则,允许专用网络通过 TCP 端口 {{port}} 连接。' + }, + 'auto.components.mobile.WindowsFirewallNotice.missing-title': { + zh: '允许手机通过 Windows 防火墙连接' + }, + 'auto.components.mobile.WindowsFirewallNotice.open-settings': { zh: '打开网络设置' }, + 'auto.components.mobile.WindowsFirewallNotice.public-description': { + zh: '允许 Orca Mobile 连接前,请将此受信任的 Wi-Fi 网络更改为“专用”。' + }, + 'auto.components.mobile.WindowsFirewallNotice.public-title': { + zh: 'Windows 将此网络标记为公用网络' + }, + 'auto.components.mobile.WindowsFirewallNotice.repair': { zh: '修复防火墙访问权限' }, + 'auto.components.mobile.WindowsFirewallNotice.repair-failed': { + zh: '无法更新 Windows 防火墙规则' + }, + 'auto.components.mobile.WindowsFirewallNotice.repair-success': { + zh: 'Windows 防火墙现在允许 Orca Mobile 在专用网络上连接' + }, + 'auto.components.mobile.WindowsFirewallNotice.repair-unverified': { + zh: '无法验证 Windows 防火墙访问权限' + }, + 'auto.components.mobile.WindowsFirewallNotice.waiting': { zh: '正在等待 Windows…' }, + 'auto.components.mobile.slides.HomeSlide.00a6903322': { zh: '已启动智能体' }, + 'auto.components.mobile.slides.HomeSlide.091355da3d': { zh: 'M1 Mini · home' }, + 'auto.components.mobile.slides.HomeSlide.0bc1881bc4': { zh: '已连接 · 40 个工作树 · 5 个活跃' }, + 'auto.components.mobile.slides.HomeSlide.25d6e8a491': { zh: 'feat/mobile-page' }, + 'auto.components.mobile.slides.HomeSlide.4a40af029b': { zh: '智能体工作时长' }, + 'auto.components.mobile.slides.HomeSlide.a3d5476811': { zh: '5 小时' }, + 'auto.components.mobile.slides.HomeSlide.a7d9e2c44d': { zh: '7 天' }, + 'auto.components.mobile.slides.HomeSlide.d33d7a9c29': { + zh: 'orca  ·  feat/mobile-page' + }, + 'auto.components.mobile.slides.TerminalSlide.1d448b69f7': { zh: 'PASS' }, + 'auto.components.mobile.slides.TerminalSlide.29f2d13839': { zh: '输入命令…' }, + 'auto.components.mobile.slides.TerminalSlide.2defc05141': { zh: 'dev@mac' }, + 'auto.components.mobile.slides.TerminalSlide.336c0e070e': { + zh: 'mobile/orca-mobile-sidebar-mock-v3.html' + }, + 'auto.components.mobile.slides.TerminalSlide.4930eaaae7': { zh: 'Esc' }, + 'auto.components.mobile.slides.TerminalSlide.53ff909568': { zh: 'Tab' }, + 'auto.components.mobile.slides.TerminalSlide.58a9ee6003': { + zh: '工具调用格式已经整理好。接下来需要我补上 diff 吗?' + }, + 'auto.components.mobile.slides.TerminalSlide.80cc356591': { zh: '读取' }, + 'auto.components.mobile.slides.TerminalSlide.8432787c4e': { zh: 'feat/mobile-page' }, + 'auto.components.mobile.slides.TerminalSlide.8d6516312d': { zh: '2 个终端 · Claude 活跃' }, + 'auto.components.mobile.slides.TerminalSlide.94febb0976': { zh: '源代码管理' }, + 'auto.components.mobile.slides.TerminalSlide.a6e7cdc688': { zh: 'pnpm test --filter mobile' }, + 'auto.components.mobile.slides.TerminalSlide.aa64b519c6': { + zh: '终端屏幕。Tokyonight 配色、Menlo 字体、真实的 Claude 会话' + }, + 'auto.components.mobile.slides.TerminalSlide.d6d1041a1c': { + zh: '⎿ 已将配对扫描幻灯片替换为终端会话' + }, + 'auto.components.mobile.slides.TerminalSlide.e0f98be657': { zh: 'orca/feat-mobile-page' }, + 'auto.components.mobile.slides.TerminalSlide.e4befee569': { zh: 'Shell' }, + 'auto.components.mobile.slides.TerminalSlide.e75112c834': { + zh: '我已将配对扫描幻灯片替换为高保真的' + }, + 'auto.components.mobile.slides.TerminalSlide.fc83e0d5ef': { zh: '⎿ 已读取 2103 行' }, + 'auto.components.mobile.slides.WorktreeListSlide.17f9e0d226': { zh: '最近' }, + 'auto.components.mobile.slides.WorktreeListSlide.22971156df': { zh: '仓库' }, + 'auto.components.mobile.slides.WorktreeListSlide.357a519567': { zh: '活跃' }, + 'auto.components.mobile.slides.WorktreeListSlide.c5ad56786d': { zh: '加载动画' }, + 'auto.components.settings.MobileEmulatorExamples.0820b3f84f': { zh: '试试看 — 示例提示词' }, + 'auto.components.settings.MobileEmulatorExamples.1f608e7d60': { zh: '复制提示词失败。' }, + 'auto.components.settings.MobileEmulatorExamples.2b077b5544': { zh: '已复制提示词。' }, + 'auto.components.settings.MobileEmulatorExamples.b525ff2b12': { zh: '"' }, + 'auto.components.settings.MobileEmulatorExamples.c12b253997': { zh: '复制示例提示词' }, + 'auto.components.settings.MobileEmulatorExamples.d151e25078': { zh: '"' }, + 'auto.components.settings.MobileEmulatorSdkStatus.18925b082d': { zh: '选择 SDK 文件夹' }, + 'auto.components.settings.MobileEmulatorSettingsPane.6593c9ddd3': { zh: '移动端模拟器' }, + 'auto.components.settings.MobileEmulatorSettingsPane.700ddbf9b1': { zh: '启用移动端模拟器' }, + 'auto.components.settings.MobileNetworkInterfaceSection.1e64659126': { zh: '重新生成' }, + 'auto.components.settings.MobileNetworkInterfaceSection.39fad211d9': { + zh: '通过 tailnet 在 Wi-Fi 网络外连接' + }, + 'auto.components.settings.MobileNetworkInterfaceSection.9fc5d203ff': { + zh: 'Orca Mobile 会直接连接到此电脑。若要在不同的本地网络中使用,请让电脑和手机加入同一个专用覆盖网络,再选择该网络地址生成二维码。' + }, + 'auto.components.settings.MobileNetworkInterfaceSection.b2c384cfd6': { zh: '未找到网络接口' }, + 'auto.components.settings.MobilePane.254a6d09e4': { zh: '已配对' }, + 'auto.components.settings.MobilePane.35100bca5d': { + zh: '在手机上使用终端时,Orca 会缩小终端以适配手机屏幕。关闭应用或切换到其他应用后,可选择保持手机尺寸(避免交互式 CLI 工具重新排版),或恢复为桌面尺寸。也可随时通过横幅中的“恢复此终端”或“恢复所有终端”手动调整。' + }, + 'auto.components.settings.MobilePane.c474aa09d8': { zh: '1 分钟后' }, + 'auto.components.settings.MobilePane.d4ba07d914': { zh: '5 分钟后' }, + 'auto.components.settings.MobilePane.dd3cd78d04': { zh: '使用 Orca Mobile 扫描' }, + 'auto.components.settings.MobilePane.ff865419dc': { zh: '30 分钟后' }, + 'auto.components.settings.MobileRelayStatusSection.automatic': { + zh: '手机配对后,可从任何地方连接' + }, + 'auto.components.settings.MobileRelayStatusSection.connecting': { zh: '正在连接' }, + 'auto.components.settings.MobileRelayStatusSection.directNeedsNoAccount': { + zh: '即使不登录账户,仍可通过 LAN 和 Tailscale 配对。' + }, + 'auto.components.settings.MobileRelayStatusSection.directStillAvailable': { + zh: 'LAN 和 Tailscale 连接仍然可用。' + }, + 'auto.components.settings.MobileRelayStatusSection.offline': { zh: '离线' }, + 'auto.components.settings.MobileRelayStatusSection.reconnecting': { zh: '正在重新连接' }, + 'auto.components.settings.MobileRelayStatusSection.registered': { zh: '已注册' }, + 'auto.components.settings.MobileRelayStatusSection.signIn': { zh: '登录' }, + 'auto.components.settings.MobileRelayStatusSection.signInPrompt': { + zh: '在此桌面端登录,即可从任何地方连接' + }, + 'auto.components.settings.MobileRelayStatusSection.standby': { zh: '待机 — 暂无 Relay 设备' }, + 'auto.components.settings.MobileRelayStatusSection.unavailable': { zh: '不可用' }, + 'auto.components.settings.MobileSettingsPane.174f4a3c6d': { zh: '通过手机控制终端和智能体。' }, + 'auto.components.settings.MobileSettingsPane.682293cadf': { + zh: '在左侧边栏顶部显示 Orca Mobile 按钮。' + }, + 'auto.components.settings.MobileSettingsPane.9a3c280e49': { zh: 'GitHub Releases' }, + 'auto.components.settings.MobileSettingsPane.b0088412a1': { zh: '获取;Android APK 可从' }, + 'auto.components.settings.MobileSettingsPane.b5a2ed83ff': { zh: 'App Store' }, + 'auto.components.settings.MobileSettingsPane.c8491c17ef': { + zh: '扫描二维码即可通过手机控制 Orca。此功能仍处于测试/早期预览阶段,可能存在问题或不兼容更改。iOS 应用可从' + }, + 'auto.components.settings.MobileSettingsPane.d4f2b65f30': { + zh: '在侧边栏中显示 Orca Mobile 快捷入口。' + } +} diff --git a/config/scripts/locale-zh-manual-source-control-key-overrides.mjs b/config/scripts/locale-zh-manual-source-control-key-overrides.mjs new file mode 100644 index 000000000000..bffd3072563f --- /dev/null +++ b/config/scripts/locale-zh-manual-source-control-key-overrides.mjs @@ -0,0 +1,247 @@ +// Human-reviewed Simplified Chinese for right-sidebar source control, checks, and repository tooling. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_SOURCE_CONTROL_KEY_OVERRIDES = { + // Right sidebar, source control, checks, and repository tooling. + 'auto.components.right.sidebar.BulkActionBar.ef5f5bd06e': { zh: '暂存(' }, + 'auto.components.right.sidebar.SearchHeader.464ae3974f': { zh: '区分大小写' }, + 'auto.components.right.sidebar.SearchResultItems.cc06595a3b': { zh: '复制带行号的路径' }, + 'auto.components.right.sidebar.PortsPanel.17bea6e391': { zh: 'localhost' }, + 'auto.components.right.sidebar.PortsPanel.5dd86dcf2f': { zh: '进程' }, + 'auto.components.right.sidebar.PortsPanel.729be0b4e5': { zh: '类型' }, + 'auto.components.right.sidebar.PortsPanel.935dda7718': { zh: '当前工作区' }, + 'auto.components.right.sidebar.PortsPanel.c9d106547a': { zh: '转发' }, + 'auto.components.right.sidebar.PortsPanel.d32820d3e2': { zh: '外部' }, + 'auto.components.right.sidebar.PortsPanel.ddbe58d74e': { zh: '已转发' }, + 'auto.components.right.sidebar.AiVaultPanel.originalPaneUnavailable': { zh: '原窗格已不可用。' }, + 'auto.components.right.sidebar.AiVaultPanel.transcriptsSkipped': { + zh: '已跳过 {{count}} 份对话记录' + }, + 'auto.components.right.sidebar.AiVaultSessionDetails.jumpToOriginalPane': { zh: '跳转到原面板' }, + 'auto.components.right.sidebar.AiVaultSessionDetails.noReadablePreview': { + zh: '此对话记录中没有可读的消息预览。' + }, + 'auto.components.right.sidebar.AiVaultSessionDetails.originalAsk': { zh: '初始请求' }, + 'auto.components.right.sidebar.AiVaultSessionDetails.revealLog': { zh: '在文件夹中显示日志' }, + 'auto.components.right.sidebar.AiVaultSessionDetails.subagentTranscripts': { + zh: '{{value0}} 份子智能体对话记录' + }, + 'auto.components.right.sidebar.AiVaultSessionRow.jumpToOriginalPane': { zh: '跳转到原面板' }, + 'auto.components.right.sidebar.AiVaultSessionRow.revealLog': { zh: '在文件夹中显示日志' }, + 'auto.components.right.sidebar.FileExplorerRow.0fec99bfd7': { zh: '创建副本' }, + 'auto.components.right.sidebar.GitHistoryPanel.8232c8b2f2': { + zh: '打开提交 {{value0}}:{{value1}}' + }, + 'auto.components.right.sidebar.GitHistoryPanel.9f7535d22b': { + zh: '引用是指向该提交的分支或标签名称。只有 Git 为该提交命名了引用时才会显示。' + }, + 'auto.components.right.sidebar.GitHistoryPanel.cf7cad58d2': { zh: '暂无提交' }, + 'auto.components.right.sidebar.GitHistoryPanel.d0fb0f4bf2': { zh: '刷新提交记录' }, + 'auto.components.right.sidebar.GitHistoryPanel.e5e81e59a6': { zh: '调整提交列表大小' }, + 'auto.components.right.sidebar.index.0314901467': { zh: '源代码管理' }, + 'auto.components.right.sidebar.index.6306b48afd': { zh: '源代码管理' }, + 'auto.components.right.sidebar.index.83a10e3c44': { zh: '检查' }, + 'auto.components.right.sidebar.index.9fffaf17c1': { zh: '切换右侧边栏({{value0}})' }, + 'auto.components.right.sidebar.activity.bar.buttons.1fd284e931': { zh: '更多侧边栏标签页' }, + 'auto.components.right.sidebar.ChecksPanel.07871c0589': { zh: '关联另一个 PR' }, + 'auto.components.right.sidebar.ChecksPanel.192e686e57': { zh: '在 {{value0}} 上打开' }, + 'auto.components.right.sidebar.ChecksPanel.5c88c6db07': { zh: '在 {{value0}} 上打开' }, + 'auto.components.right.sidebar.ChecksPanel.7202f4a40a': { zh: '取消关联 PR' }, + 'auto.components.right.sidebar.ChecksPanel.fdb27637f2': { zh: '正在发布…' }, + 'auto.components.right.sidebar.CreatePullRequestDialog.0c9f9a568c': { + zh: '支持 Markdown 格式。点击“用 AI 生成”,可根据当前更改自动填写。' + }, + 'auto.components.right.sidebar.CreatePullRequestDialog.7a21f0dae8': { + zh: '在 {{value0}} 上打开' + }, + 'auto.components.right.sidebar.HostedReviewActions.b25f63edd7': { zh: '未关闭' }, + 'auto.components.right.sidebar.checks.panel.content.1abb17aac9': { zh: '更多' }, + 'auto.components.right.sidebar.checks.panel.content.2524d1fb83': { + zh: '完整详情中可查看日志末尾内容。' + }, + 'auto.components.right.sidebar.checks.panel.content.365254cc1b': { zh: '标记为未解决' }, + 'auto.components.right.sidebar.checks.panel.content.3916814392': { zh: '落后(基础提交:' }, + 'auto.components.right.sidebar.checks.panel.content.49731703ea': { zh: '作业' }, + 'auto.components.right.sidebar.checks.panel.content.5341023167': { zh: '检查' }, + 'auto.components.right.sidebar.checks.panel.content.5d4ebf9391': { + zh: '查看详情或启动一轮 AI 修复。' + }, + 'auto.components.right.sidebar.checks.panel.content.60186d8498': { zh: '冲突阻止此操作' }, + 'auto.components.right.sidebar.checks.panel.content.679bf2093c': { zh: '复制日志片段' }, + 'auto.components.right.sidebar.checks.panel.content.95ad090b01': { zh: '线程' }, + 'auto.components.right.sidebar.checks.panel.content.991f50c7e4': { zh: '未配置检查' }, + 'auto.components.right.sidebar.checks.panel.content.9ad98f2a17': { zh: '待处理' }, + 'auto.components.right.sidebar.checks.panel.content.ae8a04ef17': { zh: '无法获取冲突文件详情' }, + 'auto.components.right.sidebar.checks.panel.content.b652f38caf': { zh: '失败的检查' }, + 'auto.components.right.sidebar.checks.panel.content.c16762ac8c': { + zh: '以下检查和评论来自最近一次获取的结果。' + }, + 'auto.components.right.sidebar.checks.panel.content.cdbfda4dec': { zh: '注解' }, + 'auto.components.right.sidebar.checks.panel.content.d713f500b2': { zh: '日志片段' }, + 'auto.components.right.sidebar.checks.panel.content.df137989b3': { zh: '仅显示前 20 条注解' }, + 'auto.components.right.sidebar.checks.panel.content.e15a8b77ef': { + zh: '此检查没有可直接显示的详情。' + }, + 'auto.components.right.sidebar.checks.panel.content.e4e3af15ee': { zh: '查看完整详情' }, + 'auto.components.right.sidebar.checks.panel.content.f2fe8a4e8f': { zh: '注解' }, + 'auto.components.right.sidebar.checks.panel.content.fd46a70f1a': { zh: '已启动' }, + 'auto.components.right.sidebar.checks.panel.empty.state.5f478ab3d3': { zh: '无法刷新 PR' }, + 'auto.components.right.sidebar.checks.panel.empty.state.76e15946a9': { + zh: '分支有未推送的提交' + }, + 'auto.components.right.sidebar.checks.panel.empty.state.7c299df37b': { zh: '未找到 PR' }, + 'auto.components.right.sidebar.checks.panel.empty.state.938b5606a6': { zh: '正在查找 PR' }, + 'auto.components.right.sidebar.SourceControl.03d238218c': { zh: '详情' }, + 'auto.components.right.sidebar.SourceControl.011f9713fc': { zh: '提交被阻止' }, + 'auto.components.right.sidebar.SourceControl.04a5d7239b': { + zh: '此仓库没有受支持的 Web 远程地址' + }, + 'auto.components.right.sidebar.SourceControl.054ead86b1': { zh: '使用 AI 修复提交失败' }, + 'auto.components.right.sidebar.SourceControl.0d0a8359d3': { zh: '提交信息' }, + 'auto.components.right.sidebar.SourceControl.11b5dd8e41': { zh: '比较基准:' }, + 'auto.components.right.sidebar.SourceControl.1406954883': { zh: '清除所有笔记…' }, + 'auto.components.right.sidebar.SourceControl.1f7119f604': { zh: '目标分支' }, + 'auto.components.right.sidebar.SourceControl.27a50fe970': { zh: '查看冲突' }, + 'auto.components.right.sidebar.SourceControl.2f609a2e7c': { zh: '删除所有未跟踪文件' }, + 'auto.components.right.sidebar.SourceControl.2fe2a67580': { zh: '更多笔记操作' }, + 'auto.components.right.sidebar.SourceControl.30b8d4f181': { zh: '使用 AI 修复提交失败' }, + 'auto.components.right.sidebar.SourceControl.3278b2767b': { zh: '领先' }, + 'auto.components.right.sidebar.SourceControl.3eb9b2805e': { zh: '打开 {{value0}} 上的笔记' }, + 'auto.components.right.sidebar.SourceControl.461575b9bc': { zh: '使用 AI 生成提交信息' }, + 'auto.components.right.sidebar.SourceControl.473f18758e': { zh: '源代码管理 AI 设置' }, + 'auto.components.right.sidebar.SourceControl.476b77745b': { zh: '更改比较基准' }, + 'auto.components.right.sidebar.SourceControl.493f963029': { zh: '更改比较基准' }, + 'auto.components.right.sidebar.SourceControl.4b37ae99b0': { + zh: '启动默认智能体来修复此次提交失败' + }, + 'auto.components.right.sidebar.SourceControl.574d2f4413': { zh: '清除笔记' }, + 'auto.components.right.sidebar.SourceControl.59654650d3': { zh: '清除 {{value0}} 的笔记' }, + 'auto.components.right.sidebar.SourceControl.655633c08a': { zh: '已发送' }, + 'auto.components.right.sidebar.SourceControl.6b122529d4': { zh: '生成提交信息' }, + 'auto.components.right.sidebar.SourceControl.72f2bea3f4': { zh: '展开笔记' }, + 'auto.components.right.sidebar.SourceControl.78ce2d37ac': { zh: '推送到派生仓库' }, + 'auto.components.right.sidebar.SourceControl.7a09d7f9d2': { zh: '基础分支' }, + 'auto.components.right.sidebar.SourceControl.812cb992ee': { zh: '在 {{value0}} 上打开' }, + 'auto.components.right.sidebar.SourceControl.8a5ba6a988': { zh: '无法加载提交差异' }, + 'auto.components.right.sidebar.SourceControl.8cde1a2fb0': { zh: '暂存' }, + 'auto.components.right.sidebar.SourceControl.8d8f5c6c94': { zh: '正在生成提交信息…' }, + 'auto.components.right.sidebar.SourceControl.94c42b252e': { zh: 'MD' }, + 'auto.components.right.sidebar.SourceControl.9e5ccd00aa': { zh: '无法获取提交失败的上下文' }, + 'auto.components.right.sidebar.SourceControl.9febd8ab5f': { zh: 'create_pr' }, + 'auto.components.right.sidebar.SourceControl.a9bf7c171a': { zh: '提交失败' }, + 'auto.components.right.sidebar.SourceControl.b16b8f0e4b': { zh: 'AI 提交信息' }, + 'auto.components.right.sidebar.SourceControl.b656381c18': { zh: '删除笔记' }, + 'auto.components.right.sidebar.SourceControl.b94112eb9e': { zh: '提交信息' }, + 'auto.components.right.sidebar.SourceControl.c05fe04839': { + zh: '推送到 {{value0}} 上的派生仓库(非 origin)' + }, + 'auto.components.right.sidebar.SourceControl.c085946bda': { zh: '复制第 {{value0}} 行的笔记' }, + 'auto.components.right.sidebar.SourceControl.c321542ee2': { zh: '删除第 {{value0}} 行的笔记' }, + 'auto.components.right.sidebar.SourceControl.cc199ccc5f': { zh: '更多提交和远程操作' }, + 'auto.components.right.sidebar.SourceControl.createPrIntentConfigureAi': { + zh: '请添加提交信息或配置源代码管理 AI 设置。' + }, + 'auto.components.right.sidebar.SourceControl.createPrIntentGenerateFailed': { + zh: '无法生成提交信息。请填写后重试。' + }, + 'auto.components.right.sidebar.SourceControl.createPrIntentPushing': { zh: '正在推送提交…' }, + 'auto.components.right.sidebar.SourceControl.d13edef890': { zh: '折叠笔记' }, + 'auto.components.right.sidebar.SourceControl.d7492cafce': { + zh: '无法刷新源代码管理。请重试创建 PR。' + }, + 'auto.components.right.sidebar.SourceControl.d7ae61269b': { zh: '分支上的已提交更改' }, + 'auto.components.right.sidebar.SourceControl.dd43c47089': { + zh: '选择处理此次提交失败的智能体' + }, + 'auto.components.right.sidebar.SourceControl.ddc1fbd690': { zh: '停止生成提交信息' }, + 'auto.components.right.sidebar.SourceControl.eae2d051af': { zh: '复制所有笔记' }, + 'auto.components.right.sidebar.SourceControl.ec7bfced55': { zh: '选择智能体修复提交失败' }, + 'auto.components.right.sidebar.SourceControl.f62ce91ade': { zh: 'origin' }, + 'auto.components.right.sidebar.SourceControl.f6cb48b6fe': { zh: '用 AI 解决' }, + 'auto.components.right.sidebar.SourceControl.fda060d6ce': { + zh: '请检查提交信息,然后重试创建 PR。' + }, + 'auto.components.right.sidebar.SourceControlAgentActionDialogForm.c7ff8cef11': { + zh: '正在检测智能体…' + }, + 'auto.components.right.sidebar.SourceControlAgentActionDialogForm.fe119187bb': { + zh: '--model sonnet' + }, + 'auto.components.right.sidebar.SourceControlTextGenerationDialogForm.1f6fcfb6cf': { + zh: '命令模板' + }, + 'auto.components.right.sidebar.SourceControlTextGenerationDialogForm.551ffd111b': { + zh: '--model sonnet' + }, + 'auto.components.right.sidebar.source.control.ai.commit.failure.launch.4f4e0418a0': { + zh: '无法构建智能体提示词。' + }, + 'auto.components.right.sidebar.source.control.ai.commit.failure.launch.a8b97d2318': { + zh: '已启动智能体处理提交失败。' + }, + 'auto.components.right.sidebar.source.control.ai.commit.failure.launch.f2b47026e8': { + zh: '提交失败提示词为空。请更新源代码管理 AI 设置。' + }, + 'auto.components.right.sidebar.source.control.discard.confirmation.5ddd8cac7f': { + zh: '放弃所有已暂存的更改?' + }, + 'auto.components.right.sidebar.source.control.dropdown.items.226b85a3a7': { zh: '获取' }, + 'auto.components.right.sidebar.source.control.dropdown.items.323bb614aa': { zh: '提交并同步' }, + 'auto.components.right.sidebar.source.control.primary.action.16aee3a5c1': { zh: '正在提交…' }, + 'auto.components.right.sidebar.source.control.primary.action.390abeab93': { zh: '强制推送' }, + 'auto.components.right.sidebar.source.control.primary.action.3d5dccef0b': { + zh: '没有可提交的更改。PR 已合并。' + }, + 'auto.components.right.sidebar.source.control.primary.action.95550cff15': { zh: '推送' }, + 'auto.components.right.sidebar.source.control.primary.action.ab41fb926b': { + zh: '提交已暂存的更改' + }, + 'auto.components.right.sidebar.source.control.primary.action.acce237921': { + zh: '没有可提交的更改。此分支没有可发布的更改。' + }, + 'auto.components.right.sidebar.source.control.primary.action.d64292a938': { zh: '拉取' }, + 'auto.components.right.sidebar.source.control.primary.action.e61b0d7a3c': { + zh: '请先检出分支再发布提交。' + }, + 'auto.components.right.sidebar.source.control.primary.action.f01f16d77f': { + zh: '输入提交信息后提交' + }, + 'auto.components.right.sidebar.source.control.primary.action.fa3bd4f40c': { + zh: '至少暂存一个文件后再提交' + }, + 'auto.components.rightSidebar.FolderWorkspacePrChecksPanel.reviewChecks': { zh: '查看检查' }, + + // Additional key-specific corrections found during full-catalog human review. + 'auto.components.right.sidebar.checks.panel.content.7c1f0a2b11': { zh: '未解决' }, + + 'auto.components.right.sidebar.GitHistoryPanel.9289ba0cb9': { zh: '什么是 Git 引用?' }, + + 'auto.components.rightSidebar.FolderWorkspaceWorktreesPanel.unavailable': { + zh: '只有文件夹工作区才会显示此面板。' + }, + + 'auto.components.right.sidebar.source.control.ai.push.failure.launch.f2b47026e8': { + zh: '推送失败提示词为空。请更新源代码管理 AI 设置。' + }, + + 'auto.components.right.sidebar.source.control.ai.recovery.launch.push.empty': { + zh: '推送失败提示词为空。请更新源代码管理 AI 设置。' + }, + + 'auto.components.right.sidebar.source.control.ai.recovery.launch.commit.empty': { + zh: '提交失败提示词为空。请更新源代码管理 AI 设置。' + }, + + 'auto.components.right.sidebar.FileExplorerBackgroundMenu.21fe46ed36': { + zh: '新建文件' + }, + + 'auto.components.right.sidebar.FileExplorerRow.37c875d827': { zh: '新建文件' }, + + 'auto.components.right.sidebar.FileExplorerToolbar.d95e30fe28': { + zh: '刷新资源管理器' + }, + + 'auto.components.right.sidebar.SourceControlAgentActionDialogForm.1d47db9bf0': { + zh: '没有已启用的智能体' + } +} diff --git a/config/scripts/locale-zh-manual-usage-analytics-key-overrides.mjs b/config/scripts/locale-zh-manual-usage-analytics-key-overrides.mjs new file mode 100644 index 000000000000..15b432280c65 --- /dev/null +++ b/config/scripts/locale-zh-manual-usage-analytics-key-overrides.mjs @@ -0,0 +1,88 @@ +// Human-reviewed Simplified Chinese for statistics and usage analytics. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_USAGE_ANALYTICS_KEY_OVERRIDES = { + // Statistics and usage analytics. + 'auto.components.stats.ClaudeUsagePane.0cb1a36d7d': { + zh: '读取本地 Claude 用量日志,显示 token、模型和会话统计。' + }, + 'auto.components.stats.ClaudeUsagePane.2b8a2f14aa': { zh: '输出 token' }, + 'auto.components.stats.ClaudeUsagePane.51ae85fa00': { + zh: '缓存复用率 = 缓存读取 token /(输入 token + 缓存读取 token)。' + }, + 'auto.components.stats.ClaudeUsagePane.5ce4842c2c': { zh: '所有本地 Claude 用量' }, + 'auto.components.stats.ClaudeUsagePane.6afacbee37': { zh: 'Claude 用量统计' }, + 'auto.components.stats.ClaudeUsagePane.7e76c84153': { zh: '最近会话' }, + 'auto.components.stats.ClaudeUsagePane.ea71fae8fc': { zh: '输入 token' }, + 'auto.components.stats.CodexUsagePane.13badcd8f2': { + zh: '读取本地 Codex 用量日志,显示 token、模型和会话统计。' + }, + 'auto.components.stats.CodexUsagePane.0cb0983c07': { zh: '最近会话' }, + 'auto.components.stats.CodexUsagePane.247c93ca92': { zh: '• 价格为推算值' }, + 'auto.components.stats.CodexUsagePane.408210470c': { zh: 'Codex 用量统计' }, + 'auto.components.stats.CodexUsagePane.4fe8820098': { zh: '所有本地 Codex 用量' }, + 'auto.components.stats.CodexUsagePane.5d8eba87bd': { zh: '输出 token' }, + 'auto.components.stats.CodexUsagePane.94ac1f1ee7': { + zh: '推理 token 仅供查看;费用只按未缓存输入、缓存输入和输出计算。' + }, + 'auto.components.stats.CodexUsagePane.e365eaa6fd': { zh: '输入 token' }, + 'auto.components.stats.OpenCodeUsagePane.040c044d39': { zh: '按模型' }, + 'auto.components.stats.OpenCodeUsagePane.048ffe4d65': { zh: '用量最高的项目:' }, + 'auto.components.stats.OpenCodeUsagePane.144a6050e9': { zh: '所有本地 OpenCode 用量' }, + 'auto.components.stats.OpenCodeUsagePane.15c34d4b08': { zh: '记录的费用' }, + 'auto.components.stats.OpenCodeUsagePane.349f7c3f5c': { zh: '总计' }, + 'auto.components.stats.OpenCodeUsagePane.4799177b1c': { zh: '最近会话' }, + 'auto.components.stats.OpenCodeUsagePane.7aa4d8ce35': { zh: '输出 token' }, + 'auto.components.stats.OpenCodeUsagePane.7e9433469a': { zh: '会话/事件' }, + 'auto.components.stats.OpenCodeUsagePane.a15206a63a': { zh: '最常用模型:' }, + 'auto.components.stats.OpenCodeUsagePane.b5ed5c9fd0': { zh: '时间范围' }, + 'auto.components.stats.OpenCodeUsagePane.b8b3522436': { + zh: '读取本地 OpenCode 用量日志,显示 token、模型和会话统计。' + }, + 'auto.components.stats.OpenCodeUsagePane.bb6363e08c': { + zh: '此范围内暂无本地 OpenCode 用量记录。' + }, + 'auto.components.stats.OpenCodeUsagePane.bea80ceae0': { zh: 'OpenCode 用量统计' }, + 'auto.components.stats.OpenCodeUsagePane.d416f5cf92': { zh: '事件' }, + 'auto.components.stats.OpenCodeUsagePane.d637a892ed': { zh: '输入 token' }, + 'auto.components.stats.OpenCodeUsagePane.e5bb23d85e': { + zh: '如果助手消息中记录了费用,此处会从本地 OpenCode 数据库读取。' + }, + 'auto.components.stats.ShareUsageButton.bce08eccb9': { zh: '分享用量' }, + 'auto.components.stats.ShareUsageCard.2d9eb39264': { zh: 'token 总数' }, + 'auto.components.stats.ShareUsageCard.66c83284cf': { zh: '每日 token' }, + 'auto.components.stats.ShareUsageCard.6adac63cfe': { zh: '轮次' }, + 'auto.components.stats.ShareUsageCard.b760c0b622': { zh: '最常用模型' }, + 'auto.components.stats.ShareUsageCard.da62578d9d': { zh: '用量' }, + 'auto.components.stats.StatsPane.42d3e0bdf7': { zh: '用量分析提供方:{{value0}}' }, + 'auto.components.stats.StatsPane.9dbec9e675': { zh: '已启动的智能体' }, + 'auto.components.stats.StatsPane.a58aba506f': { zh: '已创建的 PR' }, + 'auto.components.stats.StatsPane.c79f073d4c': { zh: '用量分析' }, + 'auto.components.stats.stats.search.0e2a0b6431': { zh: '用量' }, + 'auto.components.stats.UsageBreakdownSection.247c93ca92': { zh: '• 价格为推算值' }, + 'auto.components.stats.UsageOverviewPane.2d13e57f72': { zh: '启用 OpenCode' }, + 'auto.components.stats.UsageOverviewPane.33f7b043d2': { zh: '提供方' }, + 'auto.components.stats.UsageOverviewPane.3887b94ce5': { zh: 'token 总数' }, + 'auto.components.stats.UsageOverviewPane.49405ccc8d': { zh: '开始统计 token' }, + 'auto.components.stats.UsageOverviewPane.55c910f4f1': { zh: '- 部分模型价格不可用' }, + 'auto.components.stats.UsageOverviewPane.60002bb22f': { + zh: '暂未找到本地 Claude、Codex 或 OpenCode 用量。下次智能体会话写入 token 日志后,概览会自动显示数据。' + }, + 'auto.components.stats.UsageOverviewPane.6c00c46815': { + zh: '启用提供方后,Orca 会扫描本地智能体日志并汇总 token 用量。' + }, + 'auto.components.stats.UsageOverviewPane.70f36452d4': { zh: '缓存占比' }, + 'auto.components.stats.UsageOverviewPane.c760c481c5': { zh: '用量概览' }, + 'auto.components.stats.usage.overview.sections.1dd166c920': { zh: '更少' }, + 'auto.components.stats.usage.overview.sections.3a795542fa': { zh: '综合 token 分布' }, + 'auto.components.stats.usage.overview.sections.3bc4a01b24': { + zh: '汇总所有已启用提供方的输入、输出和缓存 token。' + }, + 'auto.components.stats.usage.overview.sections.3de9bf87fc': { zh: '暂无模型' }, + 'auto.components.stats.usage.overview.sections.4ff104da47': { zh: 'token 分布' }, + 'auto.components.stats.usage.overview.sections.52d9221dc0': { zh: '近期 token 活动热图' }, + 'auto.components.stats.usage.overview.sections.c424eb3f8e': { zh: '最高:' }, + 'auto.components.stats.usage.overview.sections.f28ff1f852': { + zh: '近期 Claude、Codex 和 OpenCode 的综合 token 活动。' + }, + 'auto.components.stats.usage.overview.sections.f6df0d7d6d': { zh: '更多' } +} diff --git a/config/scripts/locale-zh-manual-work-item-key-overrides.mjs b/config/scripts/locale-zh-manual-work-item-key-overrides.mjs new file mode 100644 index 000000000000..57073b9be2e5 --- /dev/null +++ b/config/scripts/locale-zh-manual-work-item-key-overrides.mjs @@ -0,0 +1,314 @@ +// Human-reviewed Simplified Chinese for task and hosted work-item surfaces. +// Why: these strings need key-level context; broad phrase replacement would corrupt code tokens. +export const ZH_MANUAL_WORK_ITEM_KEY_OVERRIDES = { + // Task, GitHub, GitLab, Linear, and Jira surfaces. + 'auto.components.TaskPage.0506a78337': { zh: '这将更新 GitHub 上的 PR。' }, + 'auto.components.TaskPage.0c0de0fc0e': { zh: '无法加载来自' }, + 'auto.components.TaskPage.171b7739d8': { zh: 'MR' }, + 'auto.components.TaskPage.1742eafc14': { zh: '无项目' }, + 'auto.components.TaskPage.2193a99ec1': { zh: '打开与议题关联的工作区' }, + 'auto.components.TaskPage.2abe22ef76': { + zh: '你的 token 会通过系统钥匙串加密,并存储在本机。' + }, + 'auto.components.TaskPage.33fc2bcb30': { + zh: '使用 Jira Cloud 站点 URL、Atlassian 邮箱和 API token 浏览议题。' + }, + 'auto.components.TaskPage.345b169f1f': { zh: '高' }, + 'auto.components.TaskPage.37a82eaaf8': { zh: '已合并' }, + 'auto.components.TaskPage.37e7ee311e': { zh: '编号' }, + 'auto.components.TaskPage.38139edb52': { zh: 'GitHub' }, + 'auto.components.TaskPage.3b11c8e8fc': { zh: 'array' }, + 'auto.components.TaskPage.3cb855080f': { zh: '视图' }, + 'auto.components.TaskPage.3d93316bb0': { zh: 'PR' }, + 'auto.components.TaskPage.5af6f0ae5b': { zh: '选择团队' }, + 'auto.components.TaskPage.5d4fd69a6a': { zh: '最近活跃于此 PR' }, + 'auto.components.TaskPage.592a55611b': { + zh: '尝试选择更多团队或刷新;团队筛选条件只适用于当前已获取的议题。' + }, + 'auto.components.TaskPage.5ebff3a0aa': { zh: '无' }, + 'auto.components.TaskPage.68df347677': { zh: 'you@example.com' }, + 'auto.components.TaskPage.69591944e7': { zh: '低' }, + 'auto.components.TaskPage.713179dfdc': { zh: '无优先级' }, + 'auto.components.TaskPage.7f3f7b4c18': { zh: '描述(可选,Markdown)' }, + 'auto.components.TaskPage.7fd59c18d8': { zh: '中' }, + 'auto.components.TaskPage.8a07f21e76': { zh: '健康状况' }, + 'auto.components.TaskPage.88f478cdef': { zh: '合并 PR 失败' }, + 'auto.components.TaskPage.937b29fa35': { zh: '项' }, + 'auto.components.TaskPage.9497f2787c': { zh: '创建工作区' }, + 'auto.components.TaskPage.93d5f21fc1': { zh: 'pr' }, + 'auto.components.TaskPage.9f2b4c03a6': { zh: '提交到' }, + 'auto.components.TaskPage.afc68824ff': { zh: '未找到任何状态' }, + 'auto.components.TaskPage.b6329379ca': { zh: '新建工作区' }, + 'auto.components.TaskPage.b7bae28b6a': { zh: '已显示' }, + 'auto.components.TaskPage.bbec4717ee': { zh: 'mr' }, + 'auto.components.TaskPage.c11105dac5': { zh: '新建议题' }, + 'auto.components.TaskPage.c8d5bec5f7': { zh: '优先级' }, + 'auto.components.TaskPage.cbce2bc9cd': { zh: '无' }, + 'auto.components.TaskPage.d3d0998b7d': { zh: '新建 GitHub 议题' }, + 'auto.components.TaskPage.d45a910c4a': { zh: '更改 Linear 状态(当前为 {{value0}})' }, + 'auto.components.TaskPage.d6cda23ef1': { zh: '成员' }, + 'auto.components.TaskPage.e78ec261ed': { zh: '视图' }, + 'auto.components.TaskPage.f373ab1a4f': { zh: '紧急' }, + 'auto.components.TaskPage.ff90d0abc7': { zh: '从 {{value0}} 创建工作区' }, + 'auto.components.TaskPage.linearEmptyAttributeFilter': { + zh: '没有议题符合所选筛选条件。请清除筛选条件或尝试其他条件。' + }, + 'auto.components.taskPageEmptyState.noGitLabWorkDescription': { + zh: '没有 GitLab 工作项符合此筛选条件。' + }, + 'auto.components.taskPageEmptyState.noGitLabWorkTitle': { zh: '没有 GitLab 工作项' }, + 'auto.components.GitHubItemDialog.08d072664d': { zh: '作业' }, + 'auto.components.GitHubItemDialog.0caac1a18f': { zh: '从 PR 创建工作区' }, + 'auto.components.GitHubItemDialog.16c1abe76c': { zh: '标记为已查看' }, + 'auto.components.GitHubItemDialog.21860b58d0': { zh: '关闭拉取请求' }, + 'auto.components.GitHubItemDialog.28d0d3374f': { zh: '线程' }, + 'auto.components.GitHubItemDialog.36182aa57f': { zh: '新建工作区' }, + 'auto.components.GitHubItemDialog.3853476a97': { zh: 'GitHub 工作项' }, + 'auto.components.GitHubItemDialog.3cd5ae5b7b': { zh: '没有文件变更。' }, + 'auto.components.GitHubItemDialog.4812814bc8': { zh: '已启动' }, + 'auto.components.GitHubItemDialog.5752c25aff': { zh: '正在发布…' }, + 'auto.components.GitHubItemDialog.7d42606f66': { zh: '注解' }, + 'auto.components.GitHubItemDialog.829674460a': { + zh: '由于缺少 PR 提交的 SHA,无法显示差异。' + }, + 'auto.components.GitHubItemDialog.96d8f36798': { zh: '注解' }, + 'auto.components.GitHubItemDialog.9f88657c4e': { zh: '拉取请求已关闭' }, + 'auto.components.GitHubItemDialog.a35ea5a0f6': { zh: '已启用自动合并' }, + 'auto.components.GitHubItemDialog.aba792c8b3': { zh: '合并拉取请求失败' }, + 'auto.components.GitHubItemDialog.ba8e329d92': { zh: '取消“已查看”标记' }, + 'auto.components.GitHubItemDialog.b6f1b7adbd': { + zh: '这将在 GitHub 上重新打开拉取请求。' + }, + 'auto.components.GitHubItemDialog.c67de9e2fe': { zh: '未分配负责人' }, + 'auto.components.GitHubItemDialog.dc1ca081a8': { zh: '未关闭' }, + 'auto.components.GitHubItemDialog.e517b4d641': { zh: '已关闭' }, + 'auto.components.GitHubItemDialog.f4b1292569': { zh: '针对这些检查启动默认智能体' }, + 'auto.components.PullRequestPage.1a2570e18e': { zh: '新建工作区' }, + 'auto.components.PullRequestPage.2b4fdb880c': { zh: '取消“已查看”标记' }, + 'auto.components.PullRequestPage.345b68254c': { zh: '线程' }, + 'auto.components.PullRequestPage.3d77438c92': { + zh: '这将在 GitHub 上重新打开拉取请求。' + }, + 'auto.components.PullRequestPage.35a0573f41': { zh: '注解' }, + 'auto.components.PullRequestPage.4d18310d55': { zh: '变更文件' }, + 'auto.components.PullRequestPage.50b8fb290f': { zh: '标记为已查看' }, + 'auto.components.PullRequestPage.71a3c0f9d2': { zh: '创建工作区' }, + 'auto.components.PullRequestPage.74660bd80b': { + zh: '由于缺少 PR 提交的 SHA,无法显示差异。' + }, + 'auto.components.PullRequestPage.76551b1161': { zh: '已启动' }, + 'auto.components.PullRequestPage.7720c9c3f5': { zh: '作业' }, + 'auto.components.PullRequestPage.7aa3b5f706': { zh: '拉取请求已关闭' }, + 'auto.components.PullRequestPage.7b8f6bf6d8': { zh: '未关闭' }, + 'auto.components.PullRequestPage.8432d17901': { zh: '注解' }, + 'auto.components.PullRequestPage.894cfd884b': { zh: '正在发布…' }, + 'auto.components.PullRequestPage.96d013ed28': { zh: '关闭拉取请求' }, + 'auto.components.PullRequestPage.a459866967': { zh: '继续使用与 PR 关联的工作区' }, + 'auto.components.PullRequestPage.aae645d36d': { zh: '合并拉取请求失败' }, + 'auto.components.PullRequestPage.c9e7094a7b': { zh: '继续使用工作区' }, + 'auto.components.PullRequestPage.d65f70786e': { zh: '已关闭' }, + 'auto.components.PullRequestPage.dd5d9a4f17': { zh: '更新了 {{value0}}' }, + 'auto.components.PullRequestPage.filesRetry': { zh: '重试' }, + 'auto.components.GitLabItemDialog.007423f585': { zh: '无法获取差异内容。' }, + 'auto.components.GitLabItemDialog.02cbe2de44': { zh: '流水线' }, + 'auto.components.GitLabItemDialog.21f8dde18a': { zh: '行内评论' }, + 'auto.components.GitLabItemDialog.3c0b6ccca7': { zh: 'bug, backend' }, + 'auto.components.GitLabItemDialog.60c13320c4': { zh: '已添加行内评论' }, + 'auto.components.GitLabItemDialog.7a7204417f': { zh: '行' }, + 'auto.components.GitLabItemDialog.808b1ca1ba': { zh: '没有文件变更。' }, + 'auto.components.GitLabItemDialog.98718490e4': { zh: '必须填写 MR 标题。' }, + 'auto.components.GitLabItemDialog.ceaf7c30c7': { zh: '无法获取此 GitLab 用户的评审人 ID。' }, + 'auto.components.GitLabItemDialog.e089f62594': { zh: '已合并 MR !{{value0}}' }, + 'auto.components.GitLabItemDialog.f11e3e7675': { zh: '此 MR 没有流水线运行记录。' }, + 'auto.components.GitLabItemDialog.f7cb495a12': { zh: '已重试 {{value0}}' }, + 'auto.components.GitLabItemDialog.ffdd9a78e1': { + zh: '缺少 MR 的差异引用,无法添加行内评论。' + }, + 'auto.components.gitlab.gitlab.rate.limit.display.14e144f7a7': { zh: 'GitLab API 配额' }, + 'auto.components.gitlab.gitlab.rate.limit.display.3e2c982cfa': { zh: '剩余,距重置' }, + 'auto.components.gitlab.gitlab.rate.limit.display.a2d3d1fdde': { zh: 'GitLab API 配额不可用。' }, + 'auto.components.gitlab.gitlab.rate.limit.display.a2f68645ac': { zh: '刷新 GitLab API 配额' }, + 'auto.components.gitlab.gitlab.rate.limit.display.budget_scope_prefix': { zh: '配额范围' }, + 'auto.components.gitlab.gitlab.rate.limit.display.ebc0e8ecf1': { + zh: '正在加载 GitLab API 配额…' + }, + 'auto.components.github.IssueSourceSelector.30b2c9df91': { zh: 'upstream' }, + 'auto.components.github.IssueSourceSelector.51d1608920': { zh: 'origin' }, + 'auto.components.github.IssueSourceSelector.643d7e9496': { zh: 'upstream' }, + 'auto.components.github.IssueSourceSelector.d6aeb2012b': { zh: '议题来源:' }, + 'auto.components.github.PRFilterDropdowns.19bb6f115f': { zh: 'reviewed-by' }, + 'auto.components.github.PRFilterSections.3ce4d5e96e': { zh: 'PR' }, + 'auto.components.github.PRFilterSections.bd162b7d5a': { zh: '已合并' }, + 'auto.components.github.github.rate.limit.display.34973d4695': { zh: 'GitHub API 配额不可用。' }, + 'auto.components.github.github.rate.limit.display.5509443543': { + zh: '正在加载 GitHub API 配额…' + }, + 'auto.components.github.github.rate.limit.display.58c5f88216': { zh: 'GitHub API 配额' }, + 'auto.components.github.github.rate.limit.display.6da1858354': { zh: '剩余 · 距重置' }, + 'auto.components.github.github.rate.limit.display.budget_scope_prefix': { zh: '配额范围' }, + 'auto.components.github.github.rate.limit.display.d12d3d6f33': { zh: '刷新 GitHub API 配额' }, + 'auto.components.github.github.rate.limit.display.f42790d150': { zh: '/' }, + 'auto.components.github.githubIssueCloseReasons.completed.label': { + zh: '关闭并标记为已完成' + }, + 'auto.components.github.githubIssueCloseReasons.duplicate.label': { + zh: '作为重复议题关闭' + }, + 'auto.components.github.githubIssueCloseReasons.notPlanned.label': { + zh: '关闭并标记为不再处理' + }, + 'auto.components.github.project.ProjectCell.191905e20e': { zh: '当前及后续' }, + 'auto.components.github.project.ProjectCell.54cac64427': { zh: '该行缺少仓库 slug。' }, + 'auto.components.github.project.ProjectCell.af5d8c912a': { zh: '受限工作项' }, + 'auto.components.github.project.ProjectItemSlugDialog.4450efea9c': { zh: 'GitHub 工作项' }, + 'auto.components.github.project.ProjectPicker.43a88ae574': { zh: 'BOARD_LAYOUT' }, + 'auto.components.github.project.ProjectPicker.5113ecc298': { zh: '通过 URL 或所有者/编号添加' }, + 'auto.components.github.project.ProjectPicker.b3044b7a25': { zh: '最近' }, + 'auto.components.github.project.ProjectPicker.cafb908f34': { zh: 'TABLE_LAYOUT' }, + 'auto.components.github.project.ProjectViewList.4f57d2e0b1': { + zh: '没有工作项符合此视图的筛选条件。' + }, + 'auto.components.github.project.ProjectViewWrapper.030de75bc5': { + zh: '此工作项匹配多个所选仓库。' + }, + 'auto.components.github.project.ProjectViewWrapper.1ce21b8cff': { + zh: '此工作项不在所选仓库中。' + }, + 'auto.components.github.project.ProjectViewWrapper.22df63c393': { + zh: '当前 token 无法访问子议题数据。' + }, + 'auto.components.github.project.slug.dialog.AssigneesEditor.94a4e6e4fa': { zh: '无' }, + 'auto.components.github.project.slug.dialog.LabelsEditor.1a5366b5be': { zh: '无' }, + 'auto.components.right.sidebar.gitlab.mr.merge.state.53c6d3b7e9': { + zh: 'GitLab 显示此 MR 可以合并,但流水线仍在运行' + }, + 'auto.components.right.sidebar.gitlab.mr.merge.state.b41fbc180c': { + zh: 'GitLab 显示此 MR 可以合并,但部分流水线作业失败' + }, + 'auto.components.LinearIssueMarkdownDescriptionEditor.632096eb1c': { zh: '链接' }, + 'auto.components.LinearIssueMarkdownDescriptionEditor.ad1869bd54': { zh: '行内代码' }, + 'auto.components.LinearIssueWorkspace.f6c6381593': { zh: '复制提示词' }, + 'auto.components.LinearItemDrawer.780ea6ed89': { zh: '未找到任何状态' }, + 'auto.components.LinearItemDrawer.b2376d0179': { zh: '正在加载成员' }, + 'auto.components.LinearItemDrawer.dd304de85a': { zh: '属性' }, + 'auto.components.LinearItemDrawer.fbb90300e2': { zh: '自定义预估' }, + 'auto.components.linear.api.key.dialog.7d498f653c': { zh: '个人 API 密钥' }, + 'auto.components.linear.api.key.dialog.d56d3629f4': { + zh: '如需 Orca 显示该账户在此工作区可访问的所有团队,请选择完整访问权限。受限密钥只会显示获准的团队;访问私有团队还要求密钥所有者本身具备权限。' + }, + 'auto.components.linear.api.key.dialog.e3100b36b9': { + zh: '如果成员 API 密钥被禁用,请联系工作区管理员在工作区 API 设置中启用。' + }, + 'auto.components.linear.priority.icon.c43d3e065b': { zh: '优先级:' }, + 'auto.components.linear.project.view.surfaces.06b887d622': { zh: '仅显示前' }, + 'auto.components.linear.project.view.surfaces.3ad562bdf4': { zh: '个范围内议题' }, + 'auto.components.linear.project.view.surfaces.3be47aed6f': { zh: '优先级' }, + 'auto.components.linear.project.view.surfaces.65bda65159': { zh: '成员' }, + 'auto.components.linear.project.view.surfaces.7616c986c6': { + zh: '查看 {{value0}} 的议题' + }, + 'auto.components.linear.project.view.surfaces.8bbecb2510': { zh: '无' }, + 'auto.components.linear.project.view.surfaces.98730088a6': { + zh: '。可通过搜索或在 Linear 中打开来查看完整列表。' + }, + 'auto.components.linear.project.view.surfaces.f059181bd9': { zh: '私有' }, + 'auto.components.linear.project.view.surfaces.f5ef24cf46': { zh: '健康状况' }, + 'auto.components.linear.scope.selector.405b33c378': { zh: '已获取的团队中没有匹配项。' }, + 'auto.components.linear.scope.selector.b3488fad3c': { + zh: '未获取到团队。可访问范围取决于密钥权限、私有团队成员身份、归档状态和工作区权限,也可能是获取失败。' + }, + 'auto.components.JiraIssueWorkspace.0cc62bd690': { zh: '复制提示词' }, + 'auto.components.JiraIssueWorkspace.0f3c07a901': { zh: 'backend, bug' }, + 'auto.components.JiraIssueWorkspace.2441be6f9f': { zh: '创建工作区' }, + 'auto.components.JiraIssueWorkspace.2a829a2f00': { zh: '优先级' }, + 'auto.components.JiraIssueWorkspace.51bed73f88': { zh: '无优先级' }, + 'auto.components.JiraIssueWorkspace.693be070d0': { zh: '状态流转' }, + 'auto.components.jira.connect.dialog.3d81bf3ab3': { zh: 'API token' }, + + // Additional key-specific corrections found during full-catalog human review. + 'auto.components.TaskPage.285bc21dc5': { zh: '请修改或清除查询条件。' }, + + 'auto.components.GitLabItemDialog.00d0d25825': { + zh: '文件、行号和评论均为必填项。' + }, + + 'auto.components.JiraIssueWorkspace.38839801e8': { zh: '复制议题编号' }, + + 'auto.components.github.project.GhAuthErrorHelp.891a7d4616': { + zh: '在当前 shell 中取消设置' + }, + + 'auto.components.github.project.GhAuthErrorHelp.fd17b3019f': { + zh: '永久取消设置(PowerShell)' + }, + + 'auto.components.github.project.GhAuthErrorHelp.ae43542893': { zh: '查找设置位置' }, + + 'auto.components.github.PRFilterDropdowns.b27b7e526c': { zh: '请求评审人' }, + + 'auto.components.GitHubItemDialog.e463ec935f': { zh: '已请求重新运行检查' }, + + 'auto.components.GitHubItemDialog.ddafe851e1': { zh: '已请求重新运行检查' }, + + 'auto.components.PullRequestPage.18f2af42ac': { zh: '已请求重新运行检查' }, + + 'auto.components.PullRequestPage.5963a6a852': { zh: '已请求重新运行检查' }, + + 'auto.components.TaskPage.03da966159': { + zh: '请选择一个项目,以便验证对应 GitLab 主机的登录状态。' + }, + + 'auto.components.TaskPage.25ff84769a': { zh: '当前 Linear 范围内没有匹配的议题。' }, + + 'auto.components.TaskPage.closeAsCompleted': { zh: '关闭并标记为已完成' }, + + 'auto.components.TaskPage.closeAsNotPlanned': { zh: '关闭并标记为不再处理' }, + + 'auto.components.TaskPage.closeAsDuplicate': { zh: '作为重复议题关闭' }, + + 'auto.components.taskPageEmptyState.changeQueryDescription': { + zh: '请修改或清除查询条件。' + }, + + 'auto.components.linear.project.view.surfaces.f4c79cff5f': { + zh: '请先查看下方的工作区错误信息,再刷新。' + }, + + 'auto.components.LinearIssueWorkspace.e1e0a9bca9': { zh: '创建工作区' }, + + 'auto.components.GitHubItemDialog.307c98e8e3': { + zh: '在上方再显示 {{value0}} 行' + }, + + 'auto.components.GitHubItemDialog.showMoreLinesBelow': { + zh: '在下方再显示 {{value0}} 行' + }, + + 'auto.components.PullRequestPage.e295a78c11': { + zh: '在上方再显示 {{value0}} 行' + }, + + 'auto.components.PullRequestPage.showMoreLinesBelow': { + zh: '在下方再显示 {{value0}} 行' + }, + + 'auto.components.linear.project.view.surfaces.openInLinear': { + zh: '在 Linear 中打开 {{value0}}' + }, + + 'auto.components.TaskPage.67d881244c': { zh: '继续使用与 PR 关联的工作区' }, + + 'auto.components.GitHubItemDialog.6e43a16435': { zh: '行内' }, + + 'auto.components.PullRequestPage.e5f4a24f78': { zh: '行内' }, + + 'auto.components.GitHubItemDialog.4bd1f5b055': { zh: '检查' }, + + 'auto.components.PullRequestPage.94d95cf1f7': { zh: '检查' }, + + 'auto.components.TaskPage.a7396b05c6': { zh: '检查' }, + + 'auto.components.PullRequestPage.5edbe7eefa': { zh: '已启用自动合并' }, + + 'auto.components.TaskPage.fed317634c': { zh: '已启用自动合并' } +} diff --git a/config/scripts/locale-zh-manual-workspace-key-overrides.mjs b/config/scripts/locale-zh-manual-workspace-key-overrides.mjs new file mode 100644 index 000000000000..31008e792048 --- /dev/null +++ b/config/scripts/locale-zh-manual-workspace-key-overrides.mjs @@ -0,0 +1,555 @@ +// Human-reviewed Simplified Chinese fixes for everyday workspace UI. +// Why: full-key context distinguishes UI roles; compact JSON keeps this single data file lintable. +export const ZH_MANUAL_WORKSPACE_KEY_OVERRIDES = JSON.parse( + String.raw`{ + "app.recoverableError.rootDescription":{"zh":"应用界面无法完成渲染。请重试以重新加载;若问题仍然存在,请重启 Orca。"}, + "app.recoverableError.webDescription":{"zh":"请重试 Web 客户端,或重新连接已配对的运行环境。"}, + "menu.reportCrash":{"zh":"报告崩溃..."}, + "menu.showMobileButton":{"zh":"显示 Orca Mobile 按钮"}, + "menu.resetSize":{"zh":"重置窗口大小"}, + "auto.App.8a023cea1f":{"zh":"重新加载状态栏控件。"}, + "auto.App.7cbfbf622f":{"zh":"重新加载浮动工作区,或将其关闭后重新打开。"}, + "auto.App.8d1e160ed1":{"zh":"重新加载侧边栏,或切换标签页以重新载入此区域。"}, + "auto.App.03a14f6b5b":{"zh":"请重试此页面,或转到 Orca 的其他界面。"}, + "auto.App.98d4ea2823":{"zh":"此工作区的终端、浏览器或编辑器渲染失败。请重试以重新加载。"}, + "auto.App.cba0fafda5":{"zh":"当前页面仍保持打开。请重试加载列表,或切换视图。"}, + "auto.App.bdc71dddc9":{"zh":"当前工作区仍保持打开。请重试加载列表,或切换视图。"}, + "auto.App.9e0b441a91":{"zh":"显示/隐藏右侧边栏"}, + "auto.App.0a9e810705":{"zh":"重启后才会保存更改。之前的标签页已安全保存在磁盘上。"}, + "auto.App.c184e056de":{"zh":"显示/隐藏右侧边栏({{value0}})"}, + "auto.App.f7aa73e785":{"zh":"前进({{value0}})"}, + "auto.App.fe21e8f6f5":{"zh":"后退({{value0}})"}, + "auto.App.064bd07810":{"zh":"后退"}, + "auto.App.ce37cf5279":{"zh":"显示/隐藏侧边栏({{value0}})"}, + "auto.App.e4b9e7dff7":{"zh":"显示/隐藏侧边栏"}, + "auto.store.slices.browser.d175274b6d":{"zh":"新建浏览器标签页"}, + "auto.store.slices.github.f129c42773":{"zh":"GitHub 未返回新评论。"}, + "auto.store.slices.github.87020f6605":{"zh":"该行缺少所有者、仓库或编号,无法更新原始条目"}, + "auto.store.slices.github.d49ef4b944":{"zh":"保存议题来源偏好设置失败"}, + "auto.store.slices.worktrees.d1d78a7baa":{"zh":"Git 无法安全删除分支“{{value0}}”{{value1}},因此 Orca 保留了该分支,以免丢失本地提交。"}, + "auto.store.slices.worktrees.fa9299a66f":{"zh":"新工作树已是最新状态,但本地 {{value0}} 落后 {{value1}} 个{{value2}}。AI 差异可能会遗漏最近的提交。"}, + "auto.store.slices.worktrees.78e08cd877":{"zh":"Git 无法安全删除分支“{{value0}}”,因此 Orca 保留了该分支,以免丢失本地提交。"}, + "auto.store.slices.worktrees.3b57982bf6":{"zh":"删除工作区“{{value1}}”后,Git 无法安全删除分支“{{value0}}”,因此 Orca 保留了该分支,以免丢失本地提交。"}, + "auto.store.slices.worktrees.81f13f48d2":{"zh":"删除工作树“{{value1}}”后,Git 无法安全删除分支“{{value0}}”,因此 Orca 保留了该分支,以免丢失本地提交。"}, + "auto.store.slices.worktrees.runtimeScopeForbiddenDescription":{"zh":"移动端权限的配对连接无法使用工作区。请使用“设置 → 运行环境 → 共享此 Orca 服务器”中的浏览器访问链接重新连接。"}, + "auto.store.slices.repos.6d3318e813":{"zh":"导入仓库失败"}, + "auto.store.slices.repos.e649269645":{"zh":"使用“添加项目”在所选主机上输入路径。"}, + "auto.store.slices.repos.presenceProfileOverflow":{"zh":"{{names}},另有 {{count}} 个"}, + "auto.store.slices.settings.faa8fb83dd":{"zh":"切换服务器前,请保存或关闭尚未保存的编辑器标签页。"}, + "auto.hooks.useComposerState.7eb3f44ff7":{"zh":"所选智能体已禁用。创建前请选择已启用的智能体。"}, + "auto.hooks.useComposerState.b2ead86962":{"zh":"解析 PR 基准分支失败。"}, + "auto.hooks.useComposerState.3db83fc58a":{"zh":"此主机没有可用于附件的项目路径。"}, + "auto.hooks.useComposerState.setupAgentStartupPolicySaveFailed":{"zh":"保存初始化流程的智能体启动方式失败。"}, + "auto.hooks.useComposerState.5f3d2c8a1b":{"zh":"解析 MR 基准分支失败。"}, + "auto.hooks.useGlobalFileDrop.38c9f034ff":{"zh":"上传拖入的文件失败。"}, + "auto.hooks.useGlobalFileDrop.d720e2f855":{"zh":"部分拖入的文件无法上传。"}, + "auto.hooks.useGlobalFileDrop.245faa95b9":{"zh":"远程工作区没有可用于接收拖入文件的路径。"}, + "auto.hooks.useIpcEvents.0e3cf53060":{"zh":"找不到浏览器标签页 {{value0}}"}, + "auto.hooks.useIpcEvents.2f6637fe6c":{"zh":"浏览器标签页 {{value0}} 已固定"}, + "auto.hooks.useIpcEvents.a8d2bf8e9e":{"zh":"没有可关闭的当前浏览器标签页"}, + "auto.hooks.useIpcEvents.291c8ed902":{"zh":"使用远程运行环境时,浏览器标签页不可用"}, + "auto.hooks.useIpcEvents.f45fa2b03c":{"zh":"使用远程运行环境时,浏览器配置文件不可用"}, + "auto.hooks.useIpcEvents.f6300deb8b":{"zh":"新建浏览器标签页"}, + "auto.hooks.useIpcEvents.7a64b31991":{"zh":"使用远程运行环境时,无法新建本地终端"}, + "auto.hooks.useIpcEvents.60428567b4":{"zh":"使用远程运行环境时,无法显示本地终端"}, + "auto.hooks.useIpcEvents.88214a785b":{"zh":"等待本地会话状态恢复时,工作区同步超时"}, + "auto.components.sidebar.add.repo.local.start.actions.5f9ffac036":{"zh":"克隆远程 Git 仓库"}, + "auto.components.sidebar.AddRemoteHostDialog.sshDescription":{"zh":"添加一台可通过 SSH 登录并长期保存的主机。"}, + "auto.components.sidebar.AddRepoCreateStep.0ae45b8238":{"zh":"my-project"}, + "auto.components.sidebar.AddRepoCreateStep.11fd2a7db8":{"zh":"Git 仓库"}, + "auto.components.sidebar.AddRepoCreateStep.b100311784":{"zh":"输入名称后,Orca 会使用合理的默认设置创建一个完整项目。"}, + "auto.components.sidebar.AddRepoCreateStep.c234df77f7":{"zh":"创建前,请选择或输入主机上的父文件夹。"}, + "auto.components.sidebar.AddRepoCreateStep.6ed14c0281":{"zh":"未选择主机文件夹"}, + "auto.components.sidebar.AddRepoCreateStep.875dda0995":{"zh":"请输入主机上的父路径。"}, + "auto.components.sidebar.AddRepoNestedImportStep.496f68cf8c":{"zh":"正在扫描仓库。点击可停止。"}, + "auto.components.sidebar.AddRepoNestedImportStep.4df0d08cc5":{"zh":"找到"}, + "auto.components.sidebar.AddRepoNestedImportStep.8db50afe1a":{"zh":"从文件夹导入仓库"}, + "auto.components.sidebar.AddRepoNestedImportStep.220dd32d83":{"zh":"正在扫描..."}, + "auto.components.sidebar.AddRepoNestedImportStep.d75170194e":{"zh":"如果这些项目属于同一整体,例如单体仓库或一组相关仓库,请选择此项。Orca 会将它们归为一组,并让你从父文件夹开始工作。"}, + "auto.components.sidebar.AddRepoRemoteStep.80557be85a":{"zh":"选择已连接的 SSH 目标,然后输入 Git 仓库路径。"}, + "auto.components.sidebar.AddRepoRemoteStep.91b93a90a4":{"zh":"在 SSH 主机上打开项目"}, + "auto.components.sidebar.AddRepoRemoteStep.36d427bb66":{"zh":"在 SSH 主机上添加项目"}, + "auto.components.sidebar.AddRepoRemoteStep.35831a7312":{"zh":"正在添加..."}, + "auto.components.sidebar.AddRepoServerStartStep.ac66a3ed2d":{"zh":"浏览主机文件系统"}, + "auto.components.sidebar.AddRepoServerStartStep.867692f505":{"zh":"主机路径"}, + "auto.components.sidebar.AddRepoServerStartStep.423b5d3d31":{"zh":"添加所选主机上已有的 Git 仓库或文件夹。"}, + "auto.components.sidebar.AddRepoServerStartStep.3d0c035483":{"zh":"打开主机上的项目"}, + "auto.components.sidebar.AddRepoServerStartStep.438493f214":{"zh":"或手动输入主机路径"}, + "auto.components.sidebar.AddRepoServerStartStep.a2ea37d549":{"zh":"远程 Git 仓库"}, + "auto.components.sidebar.AddRepoServerStartStep.0adf083af7":{"zh":"浏览主机"}, + "auto.components.sidebar.AddRepoServerStartStep.8efa930eb5":{"zh":"从所选主机添加另一个项目。"}, + "auto.components.sidebar.AddRepoStartSteps.d301db1c9a":{"zh":"正在扫描仓库。点击可停止。"}, + "auto.components.sidebar.AddRepoSteps.2ce3f6edf8":{"zh":"/path/to/destination"}, + "auto.components.sidebar.AddRepoSteps.3d4acbe693":{"zh":"Git URL"}, + "auto.components.sidebar.AddRepoSteps.a93ef169b5":{"zh":"浏览主机文件系统"}, + "auto.components.sidebar.AddRepoSteps.df8b0e6c22":{"zh":"已在 SSH 主机上添加项目"}, + "auto.components.sidebar.AddRepoSteps.69f5b5380d":{"zh":"正在克隆..."}, + "auto.components.sidebar.AutoRenameFailedDialog.ff62a18580":{"zh":"Orca 无法根据第一条智能体消息为"}, + "auto.components.sidebar.AutoRenameFailedDialog.3afcad0497":{"zh":"生成分支名称。"}, + "auto.components.sidebar.CreateProjectLocationField.632b456b1b":{"zh":"更改"}, + "auto.components.sidebar.CreateProjectLocationField.f520f83a97":{"zh":"浏览主机文件系统"}, + "auto.components.sidebar.delete.worktree.flow.b81b4e40ca":{"zh":"如果工作区列表似乎不是最新状态,请刷新“空间”后重试。"}, + "auto.components.sidebar.delete.worktree.toast.ead7b8ee15":{"zh":"其中包含已修改的文件。仍要删除,请使用“强制删除”。"}, + "auto.components.sidebar.delete.worktree.toast.905fc8efac":{"zh":"Git 已移除此工作区。请使用“强制删除”清除 Orca 中的记录。"}, + "auto.components.sidebar.delete.worktree.toast.0899ebdb28":{"zh":"Git 已移除此工作区的记录,但目录仍保留在磁盘上。请使用“强制删除”移除这个孤立目录。"}, + "auto.components.sidebar.DeleteWorktreeDialog.dd3a45bbbd":{"zh":"下次将不再显示此确认。"}, + "auto.components.sidebar.DeleteWorktreeLineageNotice.ad407c2d55":{"zh":"更多"}, + "auto.components.sidebar.DeleteWorktreeLineageNotice.29b98bf9cd":{"zh":"删除此工作区还会删除 {{value0}} 个子工作区。"}, + "auto.components.sidebar.DeleteWorktreeSkipConfirmOption.29aefb7e52":{"zh":"不再询问"}, + "auto.components.sidebar.HostRemoveDialog.8b9c0d1e2f":{"zh":"移除主机"}, + "auto.components.sidebar.HostRemoveDialog.alsoDeleteRemoteHint":{"zh":"永久删除远程 Git 工作树及其分支。此操作不可撤销。"}, + "auto.components.sidebar.HostRemoveDialog.alsoForgetLocalHint":{"zh":"仅从 Orca 中清除。远程文件、工作树和分支均不受影响。"}, + "auto.components.sidebar.HostSectionHeaderMenu.6e7f8a9b0c":{"zh":"移除主机…"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.5a9688802a":{"zh":"再显示 {{value0}} 个"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.294de4aeb2":{"zh":"收起"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.keepOneHiddenForRepo":{"zh":"继续隐藏在 {{value0}} 中发现的 1 个工作树;可从项目菜单恢复显示"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.keepManyHiddenForRepo":{"zh":"继续隐藏在 {{value0}} 中发现的 {{count}} 个工作树;可从项目菜单恢复显示"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.keepHiddenRecover":{"zh":"继续隐藏(可从项目菜单恢复显示)"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.collapseHiddenWorktrees":{"zh":"收起 {{value0}} 中隐藏的工作树"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.expandHiddenWorktrees":{"zh":"展开 {{value0}} 中隐藏的工作树"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.hidingOneInRepo":{"zh":"已隐藏 {{value0}} 中发现的 1 个工作树"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.hidingManyInRepo":{"zh":"已隐藏 {{value0}} 中发现的 {{count}} 个工作树"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.hidingOne":{"zh":"已隐藏 1 个检测到的工作树"}, + "auto.components.sidebar.ImportedWorktreesVisibilityLine.hidingMany":{"zh":"已隐藏 {{count}} 个检测到的工作树"}, + "auto.components.sidebar.index.b826a98b6f":{"zh":"忙碌"}, + "auto.components.sidebar.NonGitFolderDialog.8fba4b8cbb":{"zh":"此文件夹不是 Git 仓库。仍可使用编辑器、终端和搜索,但无法使用基于 Git 的功能。"}, + "auto.components.sidebar.NonGitFolderDialog.c49fb13492":{"zh":"在此主机上添加文件夹失败"}, + "auto.components.sidebar.OrcaYamlTrustDialog.43b7bec4cd":{"zh":"不运行"}, + "auto.components.sidebar.OrcaYamlTrustDialog.79afc6772b":{"zh":"orca.yaml"}, + "auto.components.sidebar.OrcaYamlTrustDialog.531689199b":{"zh":"始终信任"}, + "auto.components.sidebar.OrcaYamlTrustDialog.831f2cd9f0":{"zh":"将在你的电脑上运行"}, + "auto.components.sidebar.OrcaYamlTrustDialog.aa3ffb33fb":{"zh":"此仓库的"}, + "auto.components.sidebar.ProjectGroupDeleteDialog.0e0e6764af":{"zh":"组内项目"}, + "auto.components.sidebar.ProjectGroupDeleteDialog.removeContainedProjectSingular":{"zh":"移除组内的 1 个项目"}, + "auto.components.sidebar.ProjectGroupDeleteDialog.removeContainedProjectPlural":{"zh":"移除组内的 {{value0}} 个项目"}, + "auto.components.sidebar.ProjectGroupDeleteDialog.eeabb8e8e4":{"zh":"从 Orca 中移除组内的 {{value0}} 个{{value1}}"}, + "auto.components.sidebar.ProjectGroupNameDialog.83dfbc5313":{"zh":"项目组名称"}, + "auto.components.sidebar.ProjectGroupNameDialog.4a64e78822":{"zh":"正在保存..."}, + "auto.components.sidebar.ProjectOrderManualDefaultNotice.822ff300ad":{"zh":"知道了"}, + "auto.components.sidebar.RemoteFileBrowser.2300612806":{"zh":"输入内容以筛选,或输入路径…"}, + "auto.components.sidebar.RemoteFileBrowser.971d85cc84":{"zh":"将在此主机上作为项目打开 · {{value0}}"}, + "auto.components.sidebar.RemoteFileBrowser.00c4235c10":{"zh":"没有与“{{value0}}”匹配的结果"}, + "auto.components.sidebar.RemoteFileBrowser.largeInputNoMatches":{"zh":"这段输入过长,没有匹配结果"}, + "auto.components.sidebar.RemoveFolderDialog.e62415c3d0":{"zh":"此操作只会从 Orca 中移除项目"}, + "auto.components.sidebar.RemoveFolderDialog.8c097ef04e":{"zh":";磁盘上的文件仍会保留。"}, + "auto.components.sidebar.RemoveFolderDialog.fromOrcaSsh":{"zh":";文件仍保留在 {{value0}} 上。重新添加该 SSH 主机即可恢复。"}, + "auto.components.sidebar.RemoveFolderDialog.b79b39d865":{"zh":"移除项目"}, + "auto.components.sidebar.SetupScriptPromptCard.ff1e819a11":{"zh":"添加初始化脚本"}, + "auto.components.sidebar.SetupScriptPromptCard.70715947fb":{"zh":"初始化脚本不能为空"}, + "auto.components.sidebar.SetupScriptPromptCard.888b83bf78":{"zh":"保存初始化脚本失败"}, + "auto.components.sidebar.SetupScriptPromptCard.d9f2db2738":{"zh":"项目设置"}, + "auto.components.sidebar.SetupScriptPromptCard.a5bb8c5135":{"zh":"已保存到"}, + "auto.components.sidebar.SetupScriptPromptCardViews.96a7f4198c":{"zh":"保存本地初始化脚本"}, + "auto.components.sidebar.SetupScriptPromptCardViews.0a98169776":{"zh":"添加一条在 Orca 创建新工作树时运行的初始化命令。"}, + "auto.components.sidebar.SetupScriptPromptCardViews.8349e3fa4c":{"zh":"。保存后将在新工作树中运行。"}, + "auto.components.sidebar.SetupScriptPromptCardViews.b56d1322f7":{"zh":"在以下位置找到初始化命令:"}, + "auto.components.sidebar.SetupScriptPromptCardViews.8f6be51aa1":{"zh":"orca.yaml"}, + "auto.components.sidebar.SetupScriptPromptCardViews.fdbc6cb064":{"zh":"检测到初始化脚本"}, + "auto.components.sidebar.SetupScriptPromptCardViews.7275f674cc":{"zh":"检测到初始化配置"}, + "auto.components.sidebar.SetupScriptPromptCardViews.5bfd5c8779":{"zh":"关闭初始化脚本提示"}, + "auto.components.sidebar.sidebar.project.drop.669e12dd97":{"zh":"本地文件夹和 Git 仓库"}, + "auto.components.sidebar.sidebar.project.drop.ffc769ca29":{"zh":"拖入文件夹以添加项目"}, + "auto.components.sidebar.sidebar.project.drop.740e8d0d46":{"zh":"要输入主机路径,请使用“添加项目”"}, + "auto.components.sidebar.sidebar.project.drop.d0f8943f8b":{"zh":"正在准备项目添加流程"}, + "auto.components.sidebar.sidebar.project.drop.18d3cf40e9":{"zh":"正在检查文件夹"}, + "auto.components.sidebar.SidebarFeedbackDialog.a828fa4aee":{"zh":"告诉我们哪些功能好用、哪些出了问题,或你希望 Orca 下一步做什么。"}, + "auto.components.sidebar.SidebarFilter.f506a1262a":{"zh":"筛选工作区"}, + "auto.components.sidebar.SidebarHeader.49f62c5665":{"zh":"工作区看板"}, + "auto.components.sidebar.SidebarHeader.a30e34eb5c":{"zh":"关闭工作区看板"}, + "auto.components.sidebar.SidebarNav.0c3395fd32":{"zh":"搜索工作树和浏览器标签页"}, + "auto.components.sidebar.SidebarNav.1b5c41caee":{"zh":"Orca Mobile"}, + "auto.components.sidebar.SidebarNav.9c95e1ce91":{"zh":"智能体"}, + "auto.components.sidebar.SidebarRepositoryFilterSection.f10ca29601":{"zh":"移除 {{value0}} 筛选条件"}, + "auto.components.sidebar.SidebarToolbar.87d0064026":{"zh":"工作区看板已移到底部栏"}, + "auto.components.sidebar.SidebarToolbar.a30e34eb5c":{"zh":"关闭工作区看板"}, + "auto.components.sidebar.SidebarToolbar.49f62c5665":{"zh":"工作区看板"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.95c9754653":{"zh":"智能体活动布局"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.b451c8b162":{"zh":"最近活动"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.7b316bdd51":{"zh":"手动"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.7153d07485":{"zh":"拖动工作区可调整其在各分组内的顺序。"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.b759bb87ee":{"zh":"需要处理的智能体优先,其次按最近活动排序。"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.503462f2b4":{"zh":"智能体活动"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.3728165cdd":{"zh":"名称"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.d7084e8bc8":{"zh":"智能体活动"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.b8dcc6f321":{"zh":"PR/MR 链接"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.91dfc653e8":{"zh":"GitHub 议题"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.cc17bd443b":{"zh":"详细"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.c2c7a45cda":{"zh":"无"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.65a9820bd1":{"zh":"智能体状态"}, + "auto.components.sidebar.sleep.worktree.flow.c460fecc4a":{"zh":"部分工作区休眠失败"}, + "auto.components.sidebar.sleep.worktree.flow.8bc3fc0671":{"zh":"工作区休眠失败"}, + "auto.components.sidebar.SshDisconnectedDialog.376bed88e5":{"zh":"远程主机连接出错。"}, + "auto.components.sidebar.SuppressExternalWorktreeInboxDialog.8c0b2e7a41":{"zh":"打开“非 Orca 工作树”设置"}, + "auto.components.sidebar.useAddRepoCloneFlow.4d0013cc93":{"zh":"仓库已克隆"}, + "auto.components.sidebar.useAddRepoCloneFlow.0dc4d1b657":{"zh":"请输入克隆目标的主机路径。"}, + "auto.components.sidebar.useAddRepoLocalFolderFlow.7ab10e4974":{"zh":"使用主机路径添加远程主机上的项目。"}, + "auto.components.sidebar.useAddRepoNestedImportFlow.cbfbc7a797":{"zh":"部分仓库无法导入"}, + "auto.components.sidebar.useAddRepoNestedImportFlow.1b33c5f090":{"zh":"未导入任何仓库"}, + "auto.components.sidebar.useSidebarProjectDrop.f34a286c0d":{"zh":"无法添加拖入的文件夹。"}, + "auto.components.sidebar.useSidebarProjectDrop.5ccb56c7be":{"zh":"请使用“添加项目”输入主机路径。"}, + "auto.components.sidebar.useSidebarProjectDrop.849ef13dc0":{"zh":"使用服务器运行环境时,无法通过拖放添加本地文件夹。"}, + "auto.components.sidebar.useSidebarProjectDrop.c0315153d1":{"zh":"每次只能拖入一个文件夹。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.missingCliAndSkill":{"zh":"缺少 Orca CLI 和 Linear 智能体技能。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.modalTitle":{"zh":"启用 Linear 任务访问权限"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.modalPrompt":{"zh":"允许智能体读取和编辑已关联的 Linear 任务。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.missingBoth":{"zh":"缺少 Orca CLI 和 Linear 智能体技能。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.missingSkill":{"zh":"缺少 Linear 智能体技能。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.title":{"zh":"设置 Linear 智能体技能"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.remoteCopy":{"zh":"此操作会在主机上完成安装;远程智能体环境可能需要单独安装。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.hostCopy":{"zh":"安装此技能,以便将关联的 Linear 任务交给主机智能体处理。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.dismiss":{"zh":"关闭 Linear 智能体技能设置"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.panelTitle":{"zh":"Linear 智能体技能"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.panelDescription":{"zh":"安装主机智能体技能,以便交接关联的 Linear 任务。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.terminalTitle":{"zh":"安装 Linear 智能体技能"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.terminalAria":{"zh":"Linear 智能体技能安装终端"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.successTitle":{"zh":"Linear 任务访问权限已就绪"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.successDescription":{"zh":"智能体现在可以从此工作区读取和更新已关联的 Linear 任务。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.successDescriptionWsl":{"zh":"WSL 智能体现在可以从此工作区使用已关联的 Linear 任务。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.successDescriptionRemote":{"zh":"主机智能体现在可以使用已关联的 Linear 任务。远程智能体环境可能仍需单独设置。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.successStatus":{"zh":"Linear 任务访问权限已就绪"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.wslCopy":{"zh":"安装此技能,以便将关联的 Linear 任务交给 WSL 智能体处理。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.wslLabel":{"zh":"WSL(默认)"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.toastInstallCliAndSkillDescription":{"zh":"安装 Orca CLI 和 Linear 技能,让智能体可以读取并编辑 Linear 任务。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.toastInstallCliDescription":{"zh":"安装 Orca CLI,让智能体可以读取并编辑 Linear 任务。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.toastInstallSkillDescription":{"zh":"安装 Linear 技能,让智能体可以通过 Orca CLI 读取并编辑 Linear 任务。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.toastRemoteDescription":{"zh":"{{value0}} 远程智能体环境可能仍需单独设置。"}, + "auto.components.sidebar.LinearAgentSkillSetupPrompt.toastWslDescription":{"zh":"{{value0}} 此设置会在所选 WSL 智能体运行环境中执行。"}, + "auto.components.sidebar.WorkspaceKanbanDrawerHeader.e1a34450fc":{"zh":"按状态整理工作区,并打开相应的工作区卡片。"}, + "auto.components.sidebar.WorkspaceKanbanDrawerHeader.c6a77ab0f4":{"zh":"工作区看板"}, + "auto.components.sidebar.WorkspaceKanbanSettingsMenu.34f03eb0de":{"zh":"看板设置"}, + "auto.components.sidebar.WorkspaceKanbanSettingsMenu.26cbc92150":{"zh":"工作区看板设置"}, + "auto.components.sidebar.WorkspaceKanbanStatusLane.8ad104642b":{"zh":"空"}, + "auto.components.sidebar.WorkspaceKanbanStatusLane.3611d1ae7f":{"zh":"调整工作区看板列宽"}, + "auto.components.sidebar.WorktreeCard.4f964d5e8c":{"zh":"稀疏检出"}, + "auto.components.sidebar.WorktreeCard.7d517f82e2":{"zh":"主工作树"}, + "auto.components.sidebar.WorktreeCardAgents.1b0a156717":{"zh":"智能体"}, + "auto.components.sidebar.worktree.card.compact.agents.hideOneChildAgent":{"zh":"隐藏 {{count}} 个子智能体"}, + "auto.components.sidebar.worktree.card.compact.agents.hideManyChildAgents":{"zh":"隐藏 {{count}} 个子智能体"}, + "auto.components.sidebar.worktree.card.compact.agents.showOneChildAgent":{"zh":"显示 {{count}} 个子智能体"}, + "auto.components.sidebar.worktree.card.compact.agents.showManyChildAgents":{"zh":"显示 {{count}} 个子智能体"}, + "auto.components.sidebar.WorktreeCardMeta.e42941631a":{"zh":"在 Linear 中查看"}, + "auto.components.sidebar.WorktreeCardMeta.b105fd3057":{"zh":"已关联 Linear {{value0}}"}, + "auto.components.sidebar.WorktreeCardMeta.3f2649eeb8":{"zh":"已关联议题 #{{value0}}"}, + "auto.components.sidebar.WorktreeCardMeta.openAutomationRun":{"zh":"打开运行记录"}, + "auto.components.sidebar.WorktreeCardMetadataStatusBadges.fe188062a1":{"zh":"状态:未关闭"}, + "auto.components.sidebar.workspace.status.52e3c6e2a4":{"zh":"中性灰"}, + "auto.components.sidebar.WorktreeCardMetadataStatusBadges.2931b42b09":{"zh":"状态:草稿 {{value0}}"}, + "auto.components.sidebar.WorktreeCardMetadataStatusBadges.e888362def":{"zh":"状态:已关闭"}, + "auto.components.sidebar.WorktreeCardMetadataStatusBadges.f394b3e86e":{"zh":"状态:已合并"}, + "auto.components.sidebar.WorktreeCardMetadataStatusBadges.af2b07bda5":{"zh":"状态:{{value0}}"}, + "auto.components.sidebar.WorktreeCardPorts.3240f320d7":{"zh":"活动端口"}, + "auto.components.sidebar.WorktreeCardPorts.oneLivePort":{"zh":"{{count}} 个活动端口"}, + "auto.components.sidebar.WorktreeCardPorts.manyLivePorts":{"zh":"{{count}} 个活动端口"}, + "auto.components.sidebar.WorktreeContextMenu.579b1a8e61":{"zh":"从父工作树中移除"}, + "auto.components.sidebar.WorktreeContextMenu.76865d827f":{"zh":"移至组"}, + "auto.components.sidebar.WorktreeContextMenu.503ec0f8e6":{"zh":"以此项目新建组"}, + "auto.components.sidebar.WorktreeContextMenu.f5ac91531d":{"zh":"从 Orca 中移除项目"}, + "auto.components.sidebar.WorktreeContextMenu.56cde9e8e6":{"zh":"将所选状态移至"}, + "auto.components.sidebar.WorktreeList.0c6ee14f23":{"zh":"子工作区"}, + "auto.components.sidebar.WorktreeList.c83968f87f":{"zh":"移除项目"}, + "auto.components.sidebar.WorktreeList.4a08fb55f2":{"zh":"移至组"}, + "auto.components.sidebar.WorktreeList.cbfd565f83":{"zh":"以此项目新建组"}, + "auto.components.sidebar.WorktreeList.902115cdbe":{"zh":"删除组"}, + "auto.components.sidebar.WorktreeList.045a8aed48":{"zh":"子工作区"}, + "auto.components.sidebar.WorktreeList.failedUnnestWorkspace":{"zh":"取消工作区嵌套失败"}, + "auto.components.sidebar.WorktreeList.oneContainedProjectRemained":{"zh":"删除组后,组内 1 个项目中有 {{value0}} 个仍未移除。"}, + "auto.components.sidebar.WorktreeList.manyContainedProjectsRemained":{"zh":"删除组后,组内 {{value1}} 个项目中有 {{value0}} 个仍未移除。"}, + "auto.components.sidebar.WorktreeMetaDialog.7f0be5e9a6":{"zh":"支持 **Markdown**(粗体、列表、“code”、链接)。按 Enter 或"}, + "auto.components.sidebar.WorktreeMetaDialog.b48c271d39":{"zh":"保存,按 Shift+Enter 换行。"}, + "auto.components.sidebar.WorktreeMetaDialog.030d484fc0":{"zh":"关于此工作树的笔记..."}, + "auto.components.sidebar.WorktreeMetaDialog.5ae06f40fd":{"zh":"粘贴 PR URL 或输入编号。留空可移除链接。"}, + "auto.components.sidebar.WorktreeMetaDialog.1b91db7e14":{"zh":"GH PR"}, + "auto.components.sidebar.WorktreeMetaDialog.65770ad0f0":{"zh":"编辑此工作区的 GitHub 链接和笔记。"}, + "auto.components.sidebar.WorktreeMetaDialog.382fd11a3e":{"zh":"编辑工作树详情"}, + "auto.components.sidebar.WorktreeMetaDialog.61d6f612cf":{"zh":"正在保存..."}, + "auto.components.sidebar.WorktreeOpenInMenu.8009ab69a6":{"zh":"打开方式"}, + "auto.components.sidebar.WorktreeOpenInMenu.0bed8727db":{"zh":"它可能已被移动或删除。请刷新工作区,或将其从 Orca 中移除。"}, + "auto.components.sidebar.WorktreeVisibilityDialog.25ddf19920":{"zh":"{{value0}} 个可导入"}, + "auto.components.sidebar.WorktreeVisibilityDialog.8372e4bbd9":{"zh":"当前显示 {{value0}} 个"}, + "auto.components.tab.group.TabGroupPanel.f7d6ce445e":{"zh":"关闭分组"}, + "auto.components.tab.group.AiVaultSessionDropLayer.dropOntoTerminalPane":{"zh":"拖放到终端窗格即可恢复此会话。"}, + "auto.components.tab.group.AiVaultSessionDropLayer.couldNotReadPayload":{"zh":"无法读取会话拖放数据。"}, + "auto.components.tab.group.AiVaultSessionDropLayer.sessionQueued":{"zh":"会话已加入队列"}, + "auto.components.tab.group.TabGroupDropOverlay.paneColumnLabel":{"zh":"新拆分窗格"}, + "auto.components.tab.bar.BrowserTab.9dd880bd56":{"zh":"关闭右侧标签页"}, + "auto.components.tab.bar.BrowserTab.5d6e89891f":{"zh":"复制标签页"}, + "auto.components.tab.bar.BrowserTab.911542656f":{"zh":"固定标签页"}, + "auto.components.tab.bar.BrowserTab.c5aaee8c39":{"zh":"取消固定标签页"}, + "auto.components.tab.bar.EditorFileTabContextMenu.e5ff31ccaf":{"zh":"关闭右侧标签页"}, + "auto.components.tab.bar.EditorFileTabContextMenu.ba1369dd24":{"zh":"关闭所有编辑器标签页"}, + "auto.components.tab.bar.EditorFileTabContextMenu.fdd29eb669":{"zh":"固定标签页"}, + "auto.components.tab.bar.EditorFileTabContextMenu.8e9d603a09":{"zh":"取消固定标签页"}, + "auto.components.tab.bar.QuickLaunchButton.348a04c1ad":{"zh":"智能体设置…"}, + "auto.components.tab.bar.QuickLaunchButton.e518f544b1":{"zh":"未检测到智能体"}, + "auto.components.tab.bar.QuickLaunchButton.8dea9b5cdf":{"zh":"没有已启用的智能体"}, + "auto.components.tab.bar.RecentTabSwitcher.329638ff6f":{"zh":"切换标签页"}, + "auto.components.tab.bar.RecentTabSwitcher.07ad4cd0b7":{"zh":"切换标签页"}, + "auto.components.tab.bar.SortableTab.ab19f603eb":{"zh":"重命名标签页 {{value0}}"}, + "auto.components.tab.bar.SortableTab.6df69d9388":{"zh":"关闭标签页 {{value0}}"}, + "auto.components.tab.bar.SortableTab.95db5f2f7d":{"zh":"关闭标签页"}, + "auto.components.tab.bar.SortableTabContextMenu.c1ee099c7e":{"zh":"关闭右侧标签页"}, + "auto.components.tab.bar.SortableTabContextMenu.7703990447":{"zh":"灰色"}, + "auto.components.tab.bar.SortableTabContextMenu.be905e9b0a":{"zh":"绿色"}, + "auto.components.tab.bar.SortableTabContextMenu.69682e2ce4":{"zh":"黄色"}, + "auto.components.tab.bar.SortableTabContextMenu.620aec6729":{"zh":"红色"}, + "auto.components.tab.bar.SortableTabContextMenu.03cf6dab1a":{"zh":"粉色"}, + "auto.components.tab.bar.SortableTabContextMenu.c2d8b0991f":{"zh":"紫色"}, + "auto.components.tab.bar.SortableTabContextMenu.20baa43c05":{"zh":"无"}, + "auto.components.tab.bar.SortableTabContextMenu.60f958ec75":{"zh":"固定标签页"}, + "auto.components.tab.bar.SortableTabContextMenu.417722e9c2":{"zh":"取消固定标签页"}, + "auto.components.tab.bar.TabBar.fd2b42aaa3":{"zh":"新建移动端模拟器"}, + "auto.components.tab.bar.TabBar.aea43b5748":{"zh":"打开现有的模拟器标签页。"}, + "auto.components.tab.bar.TabBar.b426bb2615":{"zh":"前往移动端模拟器"}, + "auto.components.tab.bar.TabBar.4833fb2cbe":{"zh":"新建浏览器标签页"}, + "auto.components.tab.bar.TabBar.efb33546ff":{"zh":"Git Bash"}, + "auto.components.tab.bar.TabBarCreateEntry.7cdf8ee0c8":{"zh":"打开 URL"}, + "auto.components.tab.bar.TabBarCreateEntry.b27864279e":{"zh":"启动智能体"}, + "auto.components.tab.bar.TabBarCreateEntry.39676a184c":{"zh":"打开文件、URL、智能体等"}, + "auto.components.tab.bar.TabBarQuickCommandsButton.b82e237a4b":{"zh":"更多快捷命令"}, + "auto.components.tab.bar.TabBarQuickCommandsButton.77ac113df0":{"zh":"启动 {{value0}}:{{value1}}"}, + "auto.components.tab.bar.shell.icons.d4ceaa227c":{"zh":"Git"}, + "auto.components.tab.bar.tab.create.entry.classifier.5a9c83c04b":{"zh":"打开文件、URL、智能体等"}, + "auto.components.tab.bar.TabWorkspaceLayoutMenuSection.moveToPaneColumn":{"zh":"将标签页移到其他窗格"}, + "auto.components.tab.bar.EditorFileTabCloseButton.4655cf570e":{"zh":"关闭标签页"}, + "auto.components.tab.bar.EditorFileTabCloseButton.a768f428f1":{"zh":"关闭标签页"}, + "auto.components.tab.bar.TerminalTabLeadingIcon.7ab2964bea":{"zh":"有未读的智能体完成通知"}, + "auto.components.status.bar.PetStatusSegment.59b5955621":{"zh":"上传自定义宠物…"}, + "auto.components.status.bar.PetStatusSegment.2021d4f6db":{"zh":"导入宠物包后需要完全重启应用,仅重新加载无效。"}, + "auto.components.status.bar.PetStatusSegment.e6234bcc17":{"zh":"上传自定义宠物后需要完全重启应用,仅重新加载无效。"}, + "auto.components.status.bar.ResourceUsageStatusSegment.996295bff2":{"zh":"孤立终端"}, + "auto.components.status.bar.ResourceUsageStatusSegment.27a74f91f0":{"zh":"当前没有运行中的进程"}, + "auto.components.status.bar.ResourceUsageStatusSegment.1b24a32d3a":{"zh":"内存"}, + "auto.components.status.bar.ResourceUsageStatusSegment.298f4be7f2":{"zh":"CPU"}, + "auto.components.status.bar.ResourceUsageStatusSegment.2aa2de6cb9":{"zh":"名称"}, + "auto.components.status.bar.ResourceUsageStatusSegment.6449a95c78":{"zh":"Orca 跟踪的进程当前占用的物理内存。"}, + "auto.components.status.bar.ResourceUsageStatusSegment.e7ccce7e87":{"zh":"占系统内存"}, + "auto.components.status.bar.ResourceUsageStatusSegment.9e2525c89f":{"zh":"Orca 及各工作树终端下的进程所占用的常驻内存总量。"}, + "auto.components.status.bar.ResourceUsageStatusSegment.1fedf94eae":{"zh":"CPU 总负载。超过 100% 表示同时使用了多个核心。"}, + "auto.components.status.bar.ResourceUsageStatusSegment.e7cf14ec78":{"zh":"终端会话不可用,列表可能不是最新状态。"}, + "auto.components.status.bar.ResourceUsageStatusSegment.93b0de3c21":{"zh":"重启"}, + "auto.components.status.bar.ResourceUsageStatusSegment.f8e0d794b4":{"zh":"守护进程无响应"}, + "auto.components.status.bar.ResourceUsageStatusSegment.bd19fd7a59":{"zh":"强制结束所有会话"}, + "auto.components.status.bar.ResourceUsageStatusSegment.c9382662bb":{"zh":"重启守护进程"}, + "auto.components.status.bar.ResourceUsageStatusSegment.59f178fe11":{"zh":"{{value0}},无法连接守护进程"}, + "auto.components.status.bar.ResourceUsageStatusSegment.21cacb16d1":{"zh":"· 远程"}, + "auto.components.status.bar.ResourceUsageStatusSegment.c4a8968bdd":{"zh":"展开工作区"}, + "auto.components.status.bar.ResourceUsageStatusSegment.b10695d6ce":{"zh":"强制结束会话"}, + "auto.components.status.bar.ResourceUsageStatusSegment.41ae4fa725":{"zh":"正在强制结束…"}, + "auto.components.status.bar.ResourceUsageStatusSegment.888dad8c55":{"zh":"正在加载…"}, + "auto.components.status.bar.ResourceUsageStatusSegment.6d9793d4bc":{"zh":"资源管理器:终端"}, + "auto.components.status.bar.ResourceUsageStatusSegment.ca95d077db":{"zh":"无法连接守护进程"}, + "auto.components.status.bar.ResourceUsageStatusSegment.fa6d36758d":{"zh":"强制结束会话 {{value0}}"}, + "auto.components.status.bar.ResourceUsageStatusSegment.c7e3b1a0d9f2":{"zh":"强制结束 {{value0}} 个孤立终端"}, + "auto.components.status.bar.ResourceUsageStatusSegment.d8f4c2b1e0a3":{"zh":"强制结束 {{value0}} 个孤立终端"}, + "auto.components.status.bar.StatusBar.c1df0d67ec":{"zh":"Gemini 用量"}, + "auto.components.status.bar.StatusBar.c0909c686e":{"zh":"Codex 用量"}, + "auto.components.status.bar.StatusBar.3885eb74d8":{"zh":"Claude 用量"}, + "auto.components.status.bar.StatusBar.38b5647724":{"zh":"Codex 用量运行环境"}, + "auto.components.status.bar.StatusBar.11e2354daf":{"zh":"Claude 用量运行环境"}, + "auto.components.status.bar.StatusBar.8295903d17":{"zh":"切换账户后,继续旧对话前请重启仍在运行的 Claude 终端。"}, + "auto.components.status.bar.StatusBar.5e5f9f5160":{"zh":"可用的限额重置次数:1"}, + "auto.components.status.bar.StatusBar.5ecae9197c":{"zh":"可用的限额重置次数:{{value0}}"}, + "auto.components.status.bar.StatusBar.25d8bbde69":{"zh":"正在重置…"}, + "auto.components.status.bar.StatusBar.6d1042aa6f":{"zh":"这将消耗当前账户的一次 Codex 限额重置额度,并立即重置所有符合条件的用量周期。"}, + "auto.components.status.bar.StatusBarUsageEmptyCta.84c3b15dca":{"zh":"智能体用量限制"}, + "auto.components.status.bar.tooltip.2c35eca8d4":{"zh":"无法获取用量"}, + "auto.components.status.bar.tooltip.7567cd1c6b":{"zh":"用量不可用"}, + "auto.components.status.bar.tooltip.8418ec448d":{"zh":"无法刷新 {{value0}} 用量。智能体会话可能仍处于登录状态。"}, + "auto.components.status.bar.tooltip.45198c7d95":{"zh":"可用的限额重置次数:1"}, + "auto.components.status.bar.tooltip.bce421cba3":{"zh":"可用的限额重置次数:{{value0}}"}, + "auto.components.status.bar.tooltip.7ec6e030a0":{"zh":"下一项即将到期"}, + "auto.components.status.bar.tooltip.d1e442a9e5":{"zh":"即将到期"}, + "auto.components.status.bar.tooltip.3d3c9c0c1f":{"zh":"仍在运行的 Claude 终端轮换凭据后,将刷新 Claude 用量。"}, + "auto.components.status.bar.tooltip.42fdd4da1d":{"zh":"正在刷新 Claude 登录。智能体会话可能仍处于登录状态。"}, + "auto.components.status.bar.UsagePercentageDisplayChangeNotice.title":{"zh":"用量现在显示为已用百分比"}, + "auto.components.status.bar.UsagePercentageDisplayChangeNotice.body":{"zh":"更想查看剩余百分比?可在“设置”中更改。"}, + "auto.components.status.bar.UsagePercentageDisplayChangeNotice.dismiss":{"zh":"关闭"}, + "auto.components.status.bar.UsagePercentageDisplayChangeNotice.openSettings":{"zh":"打开设置"}, + "auto.components.status.bar.UsagePercentageDisplayChangeNotice.gotIt":{"zh":"知道了"}, + "auto.components.status.bar.WorkspaceSpaceCompactPanel.9be86c46a0":{"zh":"可释放"}, + "auto.components.status.bar.WorkspaceSpaceCompactPanel.5691353a21":{"zh":"正在停止"}, + "auto.components.status.bar.WorkspaceSpaceCompactPanel.2837dc7c72":{"zh":"正在取消"}, + "auto.components.status.bar.WorkspaceSpaceCompactPanel.0583c806ac":{"zh":"尚未扫描工作区磁盘用量。"}, + "auto.components.status.bar.WorkspaceSpaceCompactPanel.39786e3b73":{"zh":"正在扫描工作区大小。"}, + "auto.components.status.bar.WorkspaceSpaceCompactPanel.3d8d47ce77":{"zh":"{{value0}} · 已保留上次结果"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.a02d84d2d2":{"zh":"正在扫描工作区。你可以离开此页面。"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.33aef3e9cc":{"zh":"大小"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.81f14d9924":{"zh":"仓库"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.697d60c456":{"zh":"清除可见项的选择"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.243287ac60":{"zh":"名称"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.6f8f6a6b04":{"zh":"筛选工作区"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.0cb1501ccf":{"zh":"可回收"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.a998501630":{"zh":"强制"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.9155381019":{"zh":"稀疏检出"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.b25c2c1086":{"zh":"顶层项目"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.d3f9c69ddc":{"zh":"缩放"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.d384a4ce9f":{"zh":"删除判定"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.720870a18e":{"zh":"保留:已关联"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.ec7b076a75":{"zh":"保留:Git 未检查"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.7ab8d7e2d7":{"zh":"保留:文件有更改"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.7f7895514e":{"zh":"保留:活动中"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.39801484e0":{"zh":"失败"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.61e25239da":{"zh":"扫描结果中没有工作区。"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.20a4204dce":{"zh":"上次成功结果仍保持显示。"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.1fce91d1b9":{"zh":"正在停止"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.d254f04097":{"zh":"正在取消"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.0990a63160":{"zh":"尚无工作区大小扫描结果。"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.oneWorkspaceRemoved":{"zh":"已从空间中移除 1 个工作区。"}, + "auto.components.status.bar.WorkspaceSpaceManagerPanel.manyWorkspacesRemoved":{"zh":"已从空间中移除 {{count}} 个工作区。"}, + "auto.components.workspace.space.WorkspaceSpacePage.8d0048e1cb":{"zh":"工作区磁盘用量和可回收的工作树存储空间。"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.352f15d6fc":{"zh":"上次活动时间"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.592fbab446":{"zh":"按最早活动时间排序"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.38ca0b1400":{"zh":"这会永久删除这些工作区的本地文件,且无法撤销。"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.9623a5107d":{"zh":"未推送的提交"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.e8b3741ff7":{"zh":"已忽略"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.4b93a235d8":{"zh":"建议清理"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.f68d538c63":{"zh":"此清理列表中没有工作区。"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.4719327c9c":{"zh":"所有清理建议均已忽略。"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.97c772c4fe":{"zh":"在已检查的仓库中未找到非活动工作区。"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.aaee139eab":{"zh":"恢复已忽略的建议"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.b299f201b9":{"zh":"可安全移除"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.8b74d4ea6e":{"zh":"正在扫描工作树和 Git 状态,并结合已打开的标签页、终端、活跃智能体及远程可用性信号,再给出删除建议。"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.7eee951968":{"zh":"正在检查工作区安全性"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.e0b5a4deaa":{"zh":"删除非活动工作区的本地文件及 Orca 状态前,请先逐项检查。"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.b2c1331844":{"zh":"删除非活动工作区"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.0c6672f5e3":{"zh":"已忽略的清理建议"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.2ddbd6fe8a":{"zh":"已检查"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.e94b1f8bb4":{"zh":"清除筛选条件"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.93b7381d50":{"zh":"筛选条件"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.ageFilter":{"zh":"闲置时间"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.sortDirection":{"zh":"排序方向"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.0a2e3c7cba":{"zh":"需确认"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.4a35c08764":{"zh":"查看"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.d1094dd529":{"zh":"待确认"}, + "auto.components.workspace.cleanup.backgroundRemoval.removed":{"zh":"已移除工作区:{{value0}}"}, + "auto.components.workspace.cleanup.backgroundRemoval.failed":{"zh":"未能移除的工作区:{{value0}}"}, + "auto.components.workspace.cleanup.backgroundRemoval.stillRemoving":{"zh":"仍在移除工作区:{{value0}}"}, + "auto.components.workspace.cleanup.backgroundRemoval.skippedPendingAncestor":{"zh":"已跳过,因为嵌套工作区尚未完成移除。"}, + "auto.components.workspace.cleanup.candidateRow.cleanGit":{"zh":"Git 无更改"}, + "auto.components.workspace.cleanup.candidateRow.diffNotesCount":{"zh":"差异笔记:{{value0}}"}, + "auto.components.workspace.cleanup.candidateRow.completedAgentsCount":{"zh":"已完成的智能体:{{value0}}"}, + "auto.components.workspace.cleanup.candidateRow.liveAgentBlocker":{"zh":"活跃智能体"}, + "auto.components.workspace.cleanup.candidateRow.volatileLocalContextBlocker":{"zh":"未持久化的本地上下文"}, + "auto.components.workspace.cleanup.candidateRow.dirtyFilesBlocker":{"zh":"有更改的文件"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandActionToggle.b0d58e37ed":{"zh":"智能体提示词"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandAppendEnterSwitch.e4e5fed3b3":{"zh":"切换是否追加 Enter"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandAppendEnterSwitch.5fa607d807":{"zh":"追加 Enter"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandDialog.97e96cc027":{"zh":"/goal"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandDialog.577a342c7d":{"zh":"让智能体检查此工作区"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandDialog.026cfb232a":{"zh":"不支持提示词命令"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandDialog.346d409ab2":{"zh":"选择智能体"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandDialog.0adba8fa0c":{"zh":"智能体"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandDialog.ed04233b3e":{"zh":"保存终端命令或智能体提示词,以便快速使用。"}, + "auto.components.terminal.quick.commands.TerminalQuickCommandDialog.dc921c17ee":{"zh":"提示词"}, + "auto.components.terminal.pane.CloseTerminalDialog.stop_agent_title":{"zh":"停止此智能体?"}, + "auto.components.terminal.pane.CloseTerminalDialog.stop_agent_description":{"zh":"关闭此终端将停止智能体当前的工作。"}, + "auto.components.terminal.pane.CloseTerminalDialog.stop_agent_confirm":{"zh":"停止智能体"}, + "auto.components.terminal.pane.MobileDriverOverlay.c6460cf584":{"zh":"收回控制权"}, + "auto.components.terminal.pane.MobileDriverOverlay.c8f2e1a4b9":{"zh":"收回此终端的控制权"}, + "auto.components.terminal.pane.MobileDriverOverlay.3eed73394f":{"zh":"电脑键盘已暂停"}, + "auto.components.terminal.pane.MobileDriverOverlay.faa367dc74":{"zh":"手机已将此终端保持为手机尺寸"}, + "auto.components.terminal.pane.MobileDriverOverlay.54f7d6f69d":{"zh":"收回所有终端的控制权"}, + "auto.components.terminal.pane.MobileDriverOverlay.f2a8b9c1d3":{"zh":"由手机控制"}, + "auto.components.terminal.pane.MobileDriverOverlay.c7e4a2b8f1":{"zh":"手机正在控制此终端"}, + "auto.components.terminal.pane.MobileDriverOverlay.d9f3c6e2a4":{"zh":"电脑键盘已暂停。收回此终端的控制权即可输入;也可收回手机控制的所有终端,或折叠面板继续查看。"}, + "auto.components.terminal.pane.MobileDriverOverlay.a6b1d8f3e2":{"zh":"手机会话已结束。可将此终端恢复为桌面尺寸,也可恢复所有仍保持手机尺寸的终端。"}, + "auto.components.terminal.pane.TerminalAgentSessionForkDialog.0c8a8629b1":{"zh":"分叉会作为独立工作区显示,而不是嵌套子项。新智能体会收到一份长度受限、可编辑的会话记录草稿。"}, + "auto.components.terminal.pane.TerminalAgentSessionForkDialog.620461df22":{"zh":"顶层分叉"}, + "auto.components.terminal.pane.TerminalAgentSessionForkDialog.619b5a35d2":{"zh":"创建顶层工作区分叉,并用捕获的上下文启动新的智能体标签页。"}, + "auto.components.terminal.pane.TerminalAgentSessionForkDialog.64e292e8e3":{"zh":"分叉智能体会话"}, + "auto.components.terminal.pane.TerminalAgentSessionForkDialog.2b10412cfc":{"zh":"正在创建..."}, + "auto.components.terminal.pane.TerminalContextMenu.b4cdd9314e":{"zh":"清屏"}, + "auto.components.terminal.pane.TerminalContextMenu.8a7ddb8b8a":{"zh":"分叉智能体会话…"}, + "auto.components.terminal.pane.TerminalContextMenu.3ce594a4a0":{"zh":"全局"}, + "auto.components.terminal.pane.TerminalErrorToast.e4aa243f8c":{"zh":"重启终端守护进程"}, + "auto.components.terminal.pane.TerminalErrorToast.a7e2fd2699":{"zh":"提交 GitHub 议题"}, + "auto.components.terminal.pane.TerminalErrorToast.cc6d997c65":{"zh":"在此重启终端守护进程,以清除残留状态。"}, + "auto.components.terminal.pane.TerminalPane.ac112e9036":{"zh":"清除标题"}, + "auto.components.terminal.pane.TerminalPane.f984ab2a30":{"zh":"清除窗格标题:{{value0}}"}, + "auto.components.terminal.pane.stale.agent.row.ad991ece5c":{"zh":"智能体所在的窗格已不可用。"}, + "auto.components.terminal.pane.stale.agent.row.090d607412":{"zh":"stale-agent-row-{{value0}}"}, + "auto.components.terminal.pane.terminal.agent.session.fork.2317900211":{"zh":"复制分叉上下文失败。"}, + "auto.components.terminal.pane.terminal.agent.session.fork.88e34d00eb":{"zh":"已在新工作区中打开顶层会话分叉"}, + "auto.components.terminal.pane.terminal.agent.session.fork.fd3d12a1e1":{"zh":"创建分叉工作区失败。"}, + "auto.components.terminal.pane.terminal.agent.session.fork.38e41edc6e":{"zh":"此工作区无法分叉为 Git 工作树。"}, + "auto.components.terminal.pane.terminal.agent.session.fork.f867385bb5":{"zh":"找不到此分叉的源工作区。"}, + "auto.components.terminal.pane.terminal.agent.session.fork.046e8d853c":{"zh":"没有可用于分叉的终端上下文"}, + "auto.components.terminal.pane.terminal.agent.session.fork.c00421d320":{"zh":"分叉上下文已复制。启动智能体并粘贴该上下文,即可开始分叉会话。"}, + "auto.components.terminal.pane.terminal.drop.handler.0c77693641":{"zh":"工作树尚未准备好,请稍后重试。"}, + "auto.components.terminal.pane.TerminalSshReconnectOverlay.connectingButton":{"zh":"正在连接..."}, + "auto.components.browser.pane.BrowserFind.fc63f336aa":{"zh":"{{value0}} / {{value1}}"}, + "auto.components.browser.pane.BrowserImportHintButton.02e89014c5":{"zh":"已从 {{value1}}{{value2}} 导入 {{value0}} 个 Cookie。"}, + "auto.components.browser.pane.BrowserMobileDriverOverlay.a6914ee43f":{"zh":"收回控制权"}, + "auto.components.browser.pane.BrowserMobileDriverOverlay.f4ecd61552":{"zh":"此标签页正由手机控制。收回控制权后即可在电脑上使用。"}, + "auto.components.browser.pane.BrowserMobileDriverOverlay.20539eca03":{"zh":"手机正在控制此浏览器"}, + "auto.components.browser.pane.BrowserPane.f30d2d35a7":{"zh":"已截图"}, + "auto.components.browser.pane.BrowserPane.f2d0c22d67":{"zh":"删除批注 {{value0}}"}, + "auto.components.browser.pane.BrowserPane.11c5084aa2":{"zh":"清除批注"}, + "auto.components.browser.pane.BrowserPane.734e4343ec":{"zh":"清除浏览器批注"}, + "auto.components.browser.pane.BrowserPane.95af781091":{"zh":"向智能体发送反馈"}, + "auto.components.browser.pane.BrowserPane.366bf5d62c":{"zh":"新标签页"}, + "auto.components.browser.pane.BrowserPane.1c78adc73d":{"zh":"在外部浏览器中打开"}, + "auto.components.browser.pane.BrowserPane.ec75d0c412":{"zh":"打开浏览器开发者工具"}, + "auto.components.browser.pane.BrowserPane.fc9be38f6f":{"zh":"为页面元素添加批注"}, + "auto.components.browser.pane.BrowserPane.1b179ab561":{"zh":"复制页面 URL"}, + "auto.components.browser.pane.BrowserPane.250a9b3e42":{"zh":"前进"}, + "auto.components.browser.pane.BrowserPane.87eb75f7d2":{"zh":"请输入有效的 http(s) 或 localhost URL。"}, + "auto.components.browser.pane.BrowserPane.27d863542c":{"zh":"浏览器批注"}, + "auto.components.browser.pane.BrowserPane.bbe8f15e83":{"zh":"此窗格由当前运行服务器渲染。"}, + "auto.components.browser.pane.BrowserPane.8b7e6d1f5a":{"zh":"浏览器批注仅适用于本地浏览器标签页。"}, + "auto.components.browser.pane.BrowserPane.deb5293610":{"zh":"远程运行环境不支持浏览器批注"}, + "auto.components.browser.pane.BrowserPane.0cb3bd6221":{"zh":"批注类型"}, + "auto.components.browser.pane.BrowserPane.8f87e6c2e5":{"zh":"类型"}, + "auto.components.browser.pane.BrowserPane.532bac48c5":{"zh":"说明希望智能体在此处做出的更改…"}, + "auto.components.browser.pane.BrowserPane.d2a7092e6e":{"zh":"批注内容"}, + "auto.components.browser.pane.BrowserPane.b472c5fe03":{"zh":"添加浏览器批注"}, + "auto.components.browser.pane.BrowserPane.b5ba6085de":{"zh":"问题"}, + "auto.components.browser.pane.BrowserPane.143204e423":{"zh":"更改"}, + "auto.components.browser.pane.BrowserPane.db325a7eeb":{"zh":"无法访问 {{value0}}"}, + "auto.components.browser.pane.BrowserPane.e72dfa268a":{"zh":"添加批注"}, + "auto.components.browser.pane.BrowserPane.168350ae6a":{"zh":"点击或将鼠标悬停在元素上,然后按 C 复制,或按 S 截图。"}, + "auto.components.browser.pane.BrowserPane.e852e20cea":{"zh":"已复制。按 S 可截图,或选择另一个元素"}, + "auto.components.browser.pane.BrowserPane.777b5bc4ec":{"zh":"点击元素即可为智能体添加反馈。"}, + "auto.components.browser.pane.BrowserPane.c8bc7f1f9e":{"zh":"已请求"}, + "auto.components.browser.pane.BrowserPane.b313a7275b":{"zh":"正在打开远程浏览器"}, + "auto.components.browser.pane.BrowserPane.5f66313863":{"zh":"批注"}, + "auto.components.browser.pane.BrowserPane.ea6af700da":{"zh":"{{value0}} 条批注"}, + "auto.components.browser.pane.BrowserPane.c13693fe27":{"zh":"{{value0}} 条批注"}, + "auto.components.browser.pane.BrowserPane.074f0ed10b":{"zh":"{{value0}} 条批注已就绪。请选择另一个元素,或复制全部反馈。"}, + "auto.components.browser.pane.BrowserPane.a2164a6e5a":{"zh":"{{value0}} 条批注已就绪。请选择另一个元素,或复制全部反馈。"}, + "auto.components.browser.pane.BrowserToolbarMenu.64f448fb6e":{"zh":"配置文件名称"}, + "auto.components.browser.pane.BrowserToolbarMenu.67e9b9fcd6":{"zh":"新建浏览器配置文件"}, + "auto.components.browser.pane.BrowserToolbarMenu.58f2c81542":{"zh":"切换"}, + "auto.components.browser.pane.BrowserToolbarMenu.fe683eb3b4":{"zh":"切换配置文件"}, + "auto.components.browser.pane.BrowserToolbarMenu.cf7cdc67ef":{"zh":"新建配置文件…"}, + "auto.components.browser.pane.BrowserToolbarMenu.6aa42813e4":{"zh":"已从 {{value1}}{{value2}} 导入 {{value0}} 个 Cookie。"}, + "auto.components.browser.pane.BrowserToolbarMenu.a7a86702b3":{"zh":"已创建并切换到 {{value0}} 配置文件"}, + "auto.components.browser.pane.BrowserToolbarMenu.4d2f9f13a7":{"zh":"创建配置文件失败。"}, + "auto.components.browser.pane.BrowserToolbarMenu.3ccd29d771":{"zh":"已切换到 {{value0}} 配置文件"}, + "auto.components.browser.pane.BrowserToolbarMenu.bf648471c5":{"zh":"正在创建…"}, + "auto.components.dashboard.DashboardAgentChildDisclosure.hideOneChildAgent":{"zh":"隐藏 {{count}} 个子智能体"}, + "auto.components.dashboard.DashboardAgentChildDisclosure.hideManyChildAgents":{"zh":"隐藏 {{count}} 个子智能体"}, + "auto.components.dashboard.DashboardAgentChildDisclosure.showOneChildAgent":{"zh":"显示 {{count}} 个子智能体"}, + "auto.components.dashboard.DashboardAgentChildDisclosure.showManyChildAgents":{"zh":"显示 {{count}} 个子智能体"}, + "auto.components.browser.pane.GrabConfirmationSheet.314a0aaa5b":{"zh":"添加为 AI 附件"}, + "auto.components.browser.pane.GrabConfirmationSheet.effd75e330":{"zh":"周边上下文"}, + "auto.components.browser.pane.GrabConfirmationSheet.7d1480fbf1":{"zh":"HTML"}, + "auto.components.browser.pane.GrabConfirmationSheet.9098b118ab":{"zh":"页面"}, + "auto.components.browser.pane.GrabConfirmationSheet.eb98a0971a":{"zh":"\""}, + "auto.components.browser.pane.GrabConfirmationSheet.d053db279d":{"zh":"role="}, + "auto.components.browser.pane.GrabConfirmationSheet.a759d8f866":{"zh":"所选元素"}, + "auto.components.browser.pane.GrabConfirmationSheet.9c6ce0632a":{"zh":"所选元素的截图"}, + "auto.components.browser.pane.GrabConfirmationSheet.50f7114f99":{"zh":"附加前请先检查。捕获的页面上下文可能包含网站上可见的内容。"}, + "auto.components.browser.pane.GrabConfirmationSheet.f3575229df":{"zh":"抓取"}, + "auto.components.browser.pane.GrabConfirmationSheet.405bb315da":{"zh":"未命名"}, + "auto.components.browser-pane.markup.copiedToast":{"zh":"标注已复制,请粘贴到智能体中({{value0}})"}, + "auto.components.browser-pane.markup.drawHint":{"zh":"在页面上绘制,然后复制标注并粘贴到智能体中。"}, + "auto.components.browser-pane.markup.hint":{"zh":"在页面上绘制,然后复制标注并粘贴到智能体中。"}, + "auto.components.workspace.cleanup.WorkspaceCleanupDialog.readyStatus":{"zh":"可清理"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.bc96dbd041":{"zh":"工作区选项({{value0}})"}, + "auto.components.sidebar.SidebarWorkspaceOptionsMenu.optionsWithActiveFilter":{"zh":"工作区选项({{value0}} 已启用)"}, + "auto.components.sidebar.WorkspaceKanbanSettingsMenu.b45b350eb0":{"zh":"向左移动 {{value0}}"}, + "auto.components.sidebar.WorkspaceKanbanSettingsMenu.moveRight":{"zh":"向右移动 {{value0}}"}, + "auto.components.sidebar.WorkspaceStatusAppearancePopover.514be2f569":{"zh":"将 {{value0}} 的颜色设置为 {{value1}}"}, "auto.components.sidebar.WorkspaceStatusAppearancePopover.setIcon":{"zh":"将 {{value0}} 的图标设置为 {{value1}}"}, "auto.components.status.bar.WorkspaceSpaceCompactPanel.bef4dc0457":{"zh":"{{value0}} 可回收 · {{value1}} 个不可用"}, "auto.components.status.bar.WorkspaceSpaceCompactPanel.reclaimableWorkspaces":{"zh":"{{value0}} 可回收 · {{value1}} 个工作区"} +}` +) diff --git a/config/scripts/locale-zh-phrase-fixes-human.mjs b/config/scripts/locale-zh-phrase-fixes-human.mjs new file mode 100644 index 000000000000..ebb845e0839f --- /dev/null +++ b/config/scripts/locale-zh-phrase-fixes-human.mjs @@ -0,0 +1,51 @@ +// Human-reviewed terminology repairs that are safe only when the English source confirms context. +export const ZH_HUMAN_PHRASE_FIXES = [ + { pattern: /开放伊/g, replacement: 'OpenAI', whenEnIncludes: 'OpenAI' }, + { pattern: /铬合金/g, replacement: 'Chrome', whenEnIncludes: 'Chrome' }, + { pattern: /鸭鸭戈/g, replacement: 'DuckDuckGo', whenEnIncludes: 'DuckDuckGo' }, + { pattern: /阿特拉斯语/g, replacement: 'Atlassian', whenEnIncludes: 'Atlassian' }, + { pattern: /麦科斯/g, replacement: 'macOS', whenEnIncludes: 'macOS' }, + { pattern: /乌班图/g, replacement: 'Ubuntu', whenEnIncludes: 'Ubuntu' }, + { pattern: /德比安/g, replacement: 'Debian', whenEnIncludes: 'Debian' }, + { pattern: /谷歌/g, replacement: 'Google', whenEnIncludes: 'Google' }, + { pattern: /爱马仕/g, replacement: 'Hermes', whenEnIncludes: 'Hermes' }, + { pattern: /尾网/g, replacement: 'tailnet', whenEnIncludes: 'tailnet' }, + { pattern: /社会保障体系/g, replacement: 'SCSS', whenEnIncludes: 'SCSS' }, + { pattern: /网页GL/g, replacement: 'WebGL', whenEnIncludes: 'WebGL' }, + { pattern: /国际化/g, replacement: 'i18n', whenEnIncludes: 'i18n' }, + { pattern: /任务提供者/g, replacement: '任务提供商', whenEnIncludes: 'task provider' }, + { pattern: /供应商/g, replacement: '提供商', whenEnIncludes: 'provider' }, + { pattern: /提供者/g, replacement: '提供商', whenEnIncludes: 'provider' }, + { + pattern: /智能体/g, + replacement: '代理', + whenEnMatches: /\b(?:ssh|proxy|authentication|credential|user)[ -]agents?\b/i + }, + { pattern: /\s*AI\s*智能体/g, replacement: '智能体', whenEnIncludes: 'AI agent' }, + { pattern: /端子/g, replacement: '终端', whenEnIncludes: 'terminal' }, + { pattern: /存储库/g, replacement: '仓库', whenEnMatches: /\brepositor(?:y|ies)\b/i }, + { pattern: /稀疏结账/g, replacement: '稀疏检出', whenEnIncludes: 'sparse checkout' }, + { pattern: /托管评论/g, replacement: '托管评审', whenEnMatches: /\bhosted[- ]review\b/i }, + { pattern: /托管审查/g, replacement: '托管评审', whenEnMatches: /\bhosted[- ]review\b/i }, + { pattern: /审查详情/g, replacement: '评审详情', whenEnIncludes: 'review details' }, + { pattern: /审查页面/g, replacement: '评审页面', whenEnIncludes: 'review page' }, + { pattern: /评论主题/g, replacement: '评审线程', whenEnIncludes: 'review thread' }, + { pattern: /工作项目/g, replacement: '工作项', whenEnIncludes: 'work item' }, + { pattern: /工作日志/g, replacement: '作业日志', whenEnIncludes: 'job log' }, + { pattern: /开放变更/g, replacement: '待处理变更', whenEnIncludes: 'open changes' }, + { pattern: /下一步行动/g, replacement: '下一步操作', whenEnIncludes: 'next action' }, + { pattern: /更多评论动作/g, replacement: '更多评论操作', whenEnIncludes: 'comment actions' }, + { pattern: /您的/g, replacement: '你的', whenEnMatches: /[\s\S]/ }, + { pattern: /您/g, replacement: '你', whenEnMatches: /[\s\S]/ }, + { pattern: /单击/g, replacement: '点击', whenEnIncludes: 'click' }, + { pattern: /\bcookies?\b/gi, replacement: 'Cookie', whenEnMatches: /\bcookies?\b/i }, + { pattern: /选项卡/g, replacement: '标签页', whenEnMatches: /\btabs?\b/i }, + { pattern: /丰富的编辑器/g, replacement: '富文本编辑器', whenEnIncludes: 'Rich Editor' }, + { pattern: /丰富模式/g, replacement: '富文本模式', whenEnIncludes: 'rich' }, + { pattern: /丰富视图/g, replacement: '富文本视图', whenEnIncludes: 'rich view' }, + { + pattern: /丰富的 Markdown 编辑器/g, + replacement: 'Markdown 富文本编辑器', + whenEnIncludes: 'rich markdown editor' + } +] diff --git a/config/scripts/verify-localization-catalog.mjs b/config/scripts/verify-localization-catalog.mjs index 23126da730f1..ea2bdb3e060a 100644 --- a/config/scripts/verify-localization-catalog.mjs +++ b/config/scripts/verify-localization-catalog.mjs @@ -146,8 +146,8 @@ function normalizeInterpolationVariables(value) { .join('|') } -function formatInconsistentFallbackVariables(inconsistentFallbackVariables) { - return inconsistentFallbackVariables +function formatInconsistentFallbackReferences(inconsistentFallbacks) { + return inconsistentFallbacks .map(({ key, references }) => { const locations = references .map( @@ -160,6 +160,27 @@ function formatInconsistentFallbackVariables(inconsistentFallbackVariables) { .join('\n\n') } +function collectInconsistentFallbackCopy(references) { + const byKey = new Map() + + for (const reference of references) { + if (typeof reference.fallback !== 'string') { + continue + } + const existing = byKey.get(reference.key) ?? [] + existing.push(reference) + byKey.set(reference.key, existing) + } + + return [...byKey.entries()] + .map(([key, keyReferences]) => ({ + key, + references: keyReferences, + uniqueFallbackCount: new Set(keyReferences.map((reference) => reference.fallback)).size + })) + .filter(({ uniqueFallbackCount }) => uniqueFallbackCount > 1) +} + function collectInconsistentFallbackVariables(references) { const byKey = new Map() @@ -407,6 +428,15 @@ export async function main(root = process.cwd(), options = parseArgs(process.arg } } + const inconsistentFallbackCopy = collectInconsistentFallbackCopy(references) + if (inconsistentFallbackCopy.length > 0) { + console.error('Localization keys are used with inconsistent string fallbacks.') + console.error('Each key must have one complete fallback message across every call site.') + console.error('') + console.error(formatInconsistentFallbackReferences(inconsistentFallbackCopy)) + return 1 + } + const missing = references.filter((reference) => !catalogKeys.has(reference.key)) if (missing.length > 0) { const missingFallbacks = referencesMissingFallbacks(missing) @@ -443,7 +473,7 @@ export async function main(root = process.cwd(), options = parseArgs(process.arg if (inconsistentFallbackVariables.length > 0) { console.error('Localization keys are used with inconsistent interpolation placeholders.') console.error('') - console.error(formatInconsistentFallbackVariables(inconsistentFallbackVariables)) + console.error(formatInconsistentFallbackReferences(inconsistentFallbackVariables)) return 1 } diff --git a/config/scripts/verify-localization-catalog.test.mjs b/config/scripts/verify-localization-catalog.test.mjs index ce148c7890f2..f0b4364a0186 100644 --- a/config/scripts/verify-localization-catalog.test.mjs +++ b/config/scripts/verify-localization-catalog.test.mjs @@ -2,7 +2,7 @@ import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import path from 'node:path' -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { main as verifyLocalizationCatalog } from './verify-localization-catalog.mjs' @@ -79,4 +79,27 @@ describe('verify-localization-catalog', () => { await expect(verifyLocalizationCatalog(root, { fix: true })).resolves.toBe(1) expect(readJson(path.join(localesDir, 'en.json'))).toEqual({}) }) + + it('rejects different complete fallbacks for one key before fix mode writes', async () => { + const { root, localesDir } = makeProject({ + sourceText: [ + "import { translate } from '@/i18n/i18n'", + "export const issues = translate('auto.example.open', 'Open {{value0}} issues', { value0: 'Orca' })", + "export const linear = translate('auto.example.open', 'Open {{value0}} in Linear', { value0: 'Orca' })" + ].join('\n') + }) + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {}) + + try { + await expect(verifyLocalizationCatalog(root, { fix: true })).resolves.toBe(1) + const output = consoleError.mock.calls.flat().join('\n') + expect(output).toContain('auto.example.open') + expect(output).toContain('Open {{value0}} issues') + expect(output).toContain('Open {{value0}} in Linear') + expect(readJson(path.join(localesDir, 'en.json'))).toEqual({}) + expect(readJson(path.join(localesDir, 'es.json'))).toEqual({}) + } finally { + consoleError.mockRestore() + } + }) }) diff --git a/docs/reference/simplified-chinese-localization.md b/docs/reference/simplified-chinese-localization.md new file mode 100644 index 000000000000..4b36090050ba --- /dev/null +++ b/docs/reference/simplified-chinese-localization.md @@ -0,0 +1,132 @@ +# Simplified Chinese Localization + +Status: Active policy + +Last reviewed: 2026-07-16 + +## Source Of Truth + +Simplified Chinese (`zh`) is Orca's human-reviewed Chinese source locale. The +Traditional Chinese catalog (`zh-TW`) is generated from it and must not be +edited independently. + +The authoritative Simplified Chinese copy lives in the localization policy +modules, not in machine-translation cache output: + +- `config/scripts/locale-zh-human-key-overrides.mjs` combines context-specific + full-key overrides. +- `config/scripts/locale-zh-human-value-overrides.mjs` contains standalone terms + that are safe to reuse only when the complete English value matches. +- `config/scripts/locale-zh-phrase-fixes-human.mjs` contains narrowly guarded + repairs for recurring legacy wording. +- `src/renderer/src/i18n/locales/zh.json` is the materialized Simplified Chinese + catalog. +- `src/renderer/src/i18n/locales/zh-TW.json` is generated from `zh.json`. + +As of 2026-07-16, the catalog has 10,804 keys. The human policy contains 2,260 +full-key overrides, 246 full-value overrides, and 40 guarded phrase fixes. All +full-key entries are unique, exist in the English catalog, and preserve their +interpolation variables. + +## No Machine Translation + +Do not use an external translation API, LLM translation pass, browser +translator, or unattended bulk translation for Simplified Chinese. In +particular, do not run: + +- `pnpm run bootstrap:zh-catalog` +- `pnpm run bootstrap:locale-catalog -- --locale zh` +- any one-off script that sends Orca copy to a translation service + +`pnpm run sync:localization-catalog` is structural rather than semantic, but it +copies new English fallbacks into locale catalogs. Do not run it blindly for +Chinese. Add reviewed Simplified Chinese overrides for every new key, then run +the repair and verification workflow below. + +## Translation Rules + +Use complete key-level translations whenever meaning depends on the call site. +Short English words such as `Open`, `Review`, `Profile`, or `Neutral` are too +ambiguous for a global replacement. + +Preserve placeholders exactly, including names such as `{{value0}}`, +`{{count}}`, and `{{term}}`. Preserve brands, provider names, CLI commands, +paths, URLs, protocol names, language names, and code literals. + +Use direct second-person copy: `你` and `你的`, never `您` or `您的`. + +Do not build translated sentences from independently translated English +fragments. Give dynamic counts and statuses a complete localization key so +Chinese word order and units can be expressed naturally. + +## Core Terminology + +| English concept | Simplified Chinese | +| -------------------- | ------------------ | +| agent / coding agent | 智能体 | +| terminal | 终端 | +| commit | 提交 | +| repository / repo | 仓库 | +| review | 评审 | +| issue | 议题 | +| worktree | 工作树 | +| workspace | 工作区 | +| browser profile | 浏览器配置文件 | +| tab | 标签页 | +| cookie | Cookie | +| source control | 源代码管理 | +| Mobile Emulator | 移动端模拟器 | +| AI usage token | token | + +Authentication credentials may retain their product spelling, such as `API +token`. AI model usage uses `token`, matching Orca's Statistics surfaces and +avoiding confusion with credentials. + + +## Simplified-To-Traditional Generation + +`config/scripts/generate-zh-tw-catalog.mjs` uses `opencc-js` with the `cn` to +`tw` conversion profile. It protects interpolation expressions before +conversion, so application variable names are unchanged. + +This is a deterministic Simplified-to-Traditional conversion, not a separate +Taiwan copywriting pass. It intentionally does not promise Taiwan-specific word +choices such as `视窗`, `重新整理`, or `快取`. Do not switch globally to a +Taiwan-phrase profile without adding protected Orca terminology and reviewing +the full catalog; generic phrase conversion can also change established terms +such as `智能体`. + +## Adding Or Changing Copy + +1. Add or update the English `translate()` fallback and catalog key. +2. Read the complete UI context, including labels composed around the value. +3. Add a full-key Simplified Chinese override in the closest concrete domain + module. Split modules before they exceed the repository's max-lines limit. +4. Preserve every interpolation variable from the English source. +5. Materialize the Simplified Chinese catalog: + + ```sh + pnpm run repair:locale-catalog -- --locale zh + ``` + +6. Generate Traditional Chinese: + + ```sh + pnpm run generate:zh-tw-catalog + ``` + +7. Run the policy and catalog checks: + + ```sh + pnpm exec vitest run --config config/vitest.config.ts \ + config/scripts/locale-translation-policy.zh-human.test.mjs \ + config/scripts/generate-zh-tw-catalog.test.mjs \ + config/scripts/verify-localization-catalog.test.mjs + pnpm run verify:localization-catalog + pnpm run verify:localization-coverage + ``` + +Before merging, also run `pnpm run typecheck`, `pnpm run lint`, and +`git diff --check`. Treat any ordinary English sentence left in `zh.json`, any +placeholder mismatch, or any direct edit that makes `zh-TW.json` stale as a +release blocker. diff --git a/package.json b/package.json index efcbf53ac089..f0dc376e1752 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,10 @@ "smoke:computer": "node config/scripts/computer-use-smoke.mjs", "verify:computer-native": "node config/scripts/verify-computer-native.mjs", "verify:cli-bin": "node config/scripts/verify-cli-bin.mjs", - "verify:localization-catalog": "node config/scripts/verify-localization-catalog.mjs", - "sync:localization-catalog": "node config/scripts/verify-localization-catalog.mjs --fix", + "verify:localization-catalog": "pnpm run verify:zh-tw-catalog && node config/scripts/verify-localization-catalog.mjs", + "sync:localization-catalog": "node config/scripts/verify-localization-catalog.mjs --fix && pnpm run generate:zh-tw-catalog", + "generate:zh-tw-catalog": "node config/scripts/generate-zh-tw-catalog.mjs --write", + "verify:zh-tw-catalog": "node config/scripts/generate-zh-tw-catalog.mjs --check", "bootstrap:locale-catalog": "node config/scripts/bootstrap-locale-catalog.mjs", "bootstrap:zh-catalog": "node config/scripts/bootstrap-zh-catalog.mjs", "bootstrap:ko-catalog": "node config/scripts/bootstrap-locale-catalog.mjs --locale ko", @@ -185,6 +187,7 @@ "marked": "^17.0.1", "mermaid": "^11.15.0", "monaco-editor": "^0.55.1", + "opencc-js": "1.4.0", "oxfmt": "^0.52.0", "oxlint": "^1.71.0", "oxlint-plugin-react-doctor": "0.2.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7af13f9c3789..ec70c4d8c179 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -272,6 +272,9 @@ importers: monaco-editor: specifier: ^0.55.1 version: 0.55.1 + opencc-js: + specifier: 1.4.0 + version: 1.4.0 oxfmt: specifier: ^0.52.0 version: 0.52.0 @@ -5521,6 +5524,9 @@ packages: resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} engines: {node: '>=20'} + opencc-js@1.4.0: + resolution: {integrity: sha512-SEb4huI5+lT4CFPA4sgs+NAOOgpcmdHZ+vYxhRLR1bQWIt6VRwpFPjWgTJAWAg1MviDcXTBe5zVoUOVxVxNK8w==} + opentype.js@2.0.0: resolution: {integrity: sha512-kCyjv6xdDY1W/jLWZ/L3QhhTlKUqDZMQ5+Jdlw12b3dXkKNpYBqqlMMj0YDQPShWFTMwgZI1hG14kN3XUDSg/A==} hasBin: true @@ -12072,6 +12078,8 @@ snapshots: powershell-utils: 0.1.0 wsl-utils: 0.3.1 + opencc-js@1.4.0: {} + opentype.js@2.0.0: {} ora@8.2.0: diff --git a/src/main/i18n/main-i18n-lazy-locale.test.ts b/src/main/i18n/main-i18n-lazy-locale.test.ts index 877c36c47e4e..62b037953bc0 100644 --- a/src/main/i18n/main-i18n-lazy-locale.test.ts +++ b/src/main/i18n/main-i18n-lazy-locale.test.ts @@ -10,14 +10,17 @@ vi.mock('electron', () => ({ } })) +import { app } from 'electron' import { UI_LANGUAGE_CHINESE, + UI_LANGUAGE_CHINESE_TRADITIONAL, UI_LANGUAGE_ENGLISH, UI_LANGUAGE_JAPANESE, UI_LANGUAGE_KOREAN, - UI_LANGUAGE_SPANISH + UI_LANGUAGE_SPANISH, + UI_LANGUAGE_SYSTEM } from '../../shared/ui-language' -import { ensureMainI18n, setMainUiLanguage, translateMain } from './main-i18n' +import { ensureMainI18n, mainI18n, setMainUiLanguage, translateMain } from './main-i18n' describe('main-i18n lazy locale loading', () => { beforeEach(async () => { @@ -48,7 +51,21 @@ describe('main-i18n lazy locale loading', () => { expect(translateMain('menu.file', 'File')).not.toBe('File') await setMainUiLanguage(UI_LANGUAGE_CHINESE) - expect(translateMain('menu.file', 'File')).not.toBe('File') + expect(translateMain('menu.settings', 'Settings')).toBe('设置') + + const traditionalLocale = await setMainUiLanguage(UI_LANGUAGE_CHINESE_TRADITIONAL) + expect(traditionalLocale).toBe('zh-TW') + expect(mainI18n.options.load).toBe('currentOnly') + expect(translateMain('menu.settings', 'Settings')).toBe('設置') + }) + + it('maps a Traditional Chinese system locale to the Traditional catalog', async () => { + vi.mocked(app.getLocale).mockReturnValueOnce('zh-HK') + + const locale = await setMainUiLanguage(UI_LANGUAGE_SYSTEM) + + expect(locale).toBe('zh-TW') + expect(translateMain('menu.settings', 'Settings')).toBe('設置') }) it('returns to English from a lazily-loaded locale', async () => { diff --git a/src/main/i18n/main-i18n.ts b/src/main/i18n/main-i18n.ts index 4904260db5c3..7c9c93299f12 100644 --- a/src/main/i18n/main-i18n.ts +++ b/src/main/i18n/main-i18n.ts @@ -24,7 +24,8 @@ const LAZY_LOCALE_LOADERS: Record< es: () => import('../../renderer/src/i18n/locales/es.json'), ja: () => import('../../renderer/src/i18n/locales/ja.json'), ko: () => import('../../renderer/src/i18n/locales/ko.json'), - zh: () => import('../../renderer/src/i18n/locales/zh.json') + zh: () => import('../../renderer/src/i18n/locales/zh.json'), + 'zh-TW': () => import('../../renderer/src/i18n/locales/zh-TW.json') } const lazyLocaleBackend: BackendModule = { @@ -58,6 +59,9 @@ export async function ensureMainI18n(): Promise { await mainI18n.use(lazyLocaleBackend).init({ fallbackLng: DEFAULT_UI_LOCALE, lng: DEFAULT_UI_LOCALE, + // Locale resolution already maps regional tags to a complete supported + // catalog; avoid loading both zh-TW and the equally large zh base catalog. + load: 'currentOnly', // Why: mark the default locale loaded with an empty resource bundle. Main // process English strings come from translateMain() fallbacks, and // partialBundledLanguages lets the backend supply non-English catalogs. diff --git a/src/renderer/src/components/GitHubItemDialog.tsx b/src/renderer/src/components/GitHubItemDialog.tsx index d076f0b325d1..6e0c4317df25 100644 --- a/src/renderer/src/components/GitHubItemDialog.tsx +++ b/src/renderer/src/components/GitHubItemDialog.tsx @@ -183,10 +183,7 @@ import { } from '@/components/github/github-pr-reviewer-candidate-filter' import { githubAvatarUrl } from '@/components/github/github-issue-comment-helpers' import { presentGitHubPRMergeState } from '@/components/github-pr-merge-state' -import { - GITHUB_PR_MERGE_METHOD_LABELS, - resolveGitHubPRMergeMethods -} from '../../../shared/github-pr-merge-methods' +import { resolveGitHubPRMergeMethods } from '../../../shared/github-pr-merge-methods' import { findGithubIssueWorkspaceAttachment, getGithubWorkItemWorkspaceAttachmentLabel @@ -2022,6 +2019,20 @@ function PRViewedCheckbox({ filePath: string onToggle: () => void }): React.JSX.Element { + // Why: action words must remain inside the translated sentence instead of + // leaking English into CJK labels. + const viewedLabel = checked + ? translate( + 'auto.components.GitHubItemDialog.unmarkFileAsViewed', + 'Unmark {{filePath}} as viewed', + { filePath } + ) + : translate( + 'auto.components.GitHubItemDialog.markFileAsViewed', + 'Mark {{filePath}} as viewed', + { filePath } + ) + return ( @@ -2029,11 +2040,7 @@ function PRViewedCheckbox({ type="button" role="checkbox" aria-checked={checked} - aria-label={translate( - 'auto.components.GitHubItemDialog.2d89a38d9d', - '{{value0}} {{value1}} as viewed', - { value0: checked ? 'Unmark' : 'Mark', value1: filePath } - )} + aria-label={viewedLabel} disabled={pending} onClick={(event) => { event.stopPropagation() @@ -2942,7 +2949,7 @@ function CommentCodeContext({ ) } aria-label={translate( - 'auto.components.GitHubItemDialog.307c98e8e3', + 'auto.components.GitHubItemDialog.showMoreLinesBelow', 'Show {{value0}} more lines below', { value0: CODE_CONTEXT_EXPAND_STEP } )} @@ -3827,6 +3834,86 @@ function ConversationTab({ ) } +function getPullRequestStateActionCopy( + nextState: 'open' | 'closed', + number: number +): { title: string; confirmLabel: string; failedMessage: string } { + // Why: close/reopen are translated as complete actions so CJK copy never + // receives an interpolated English verb. + return nextState === 'closed' + ? { + title: translate( + 'auto.components.GitHubItemDialog.confirmClosePullRequestTitle', + 'Close PR #{{number}}?', + { number } + ), + confirmLabel: translate( + 'auto.components.GitHubItemDialog.closePullRequestConfirmLabel', + 'Close' + ), + failedMessage: translate( + 'auto.components.GitHubItemDialog.failedToClosePullRequest', + 'Failed to close PR' + ) + } + : { + title: translate( + 'auto.components.GitHubItemDialog.confirmReopenPullRequestTitle', + 'Reopen PR #{{number}}?', + { number } + ), + confirmLabel: translate( + 'auto.components.GitHubItemDialog.reopenPullRequestConfirmLabel', + 'Reopen' + ), + failedMessage: translate( + 'auto.components.GitHubItemDialog.failedToReopenPullRequest', + 'Failed to reopen PR' + ) + } +} + +function getLocalizedPullRequestMergeMethodLabel(method: GitHubPRMergeMethod): string { + // Why: merge method names are visible commands, not code identifiers, and + // must be localized wherever they appear. + switch (method) { + case 'squash': + return translate('auto.components.GitHubItemDialog.squashAndMergeLabel', 'Squash and merge') + case 'merge': + return translate( + 'auto.components.GitHubItemDialog.createMergeCommitLabel', + 'Create merge commit' + ) + case 'rebase': + return translate('auto.components.GitHubItemDialog.rebaseAndMergeLabel', 'Rebase and merge') + } +} + +function getPullRequestMergeConfirmTitle(method: GitHubPRMergeMethod, number: number): string { + // Why: each method owns a complete confirmation sentence so translations + // can choose their own word order around the PR number. + switch (method) { + case 'squash': + return translate( + 'auto.components.GitHubItemDialog.confirmSquashAndMergeTitle', + 'Squash and merge PR #{{number}}?', + { number } + ) + case 'merge': + return translate( + 'auto.components.GitHubItemDialog.confirmCreateMergeCommitTitle', + 'Create merge commit for PR #{{number}}?', + { number } + ) + case 'rebase': + return translate( + 'auto.components.GitHubItemDialog.confirmRebaseAndMergeTitle', + 'Rebase and merge PR #{{number}}?', + { number } + ) + } +} + function PRActionsPanel({ item, repoPath, @@ -3891,13 +3978,9 @@ function PRActionsPanel({ if (!canMutateState || statePending) { return } - const label = nextState === 'closed' ? 'Close' : 'Reopen' + const actionCopy = getPullRequestStateActionCopy(nextState, item.number) const confirmed = await confirm({ - title: translate( - 'auto.components.GitHubItemDialog.03d7216d62', - '{{value0}} PR #{{value1}}?', - { value0: label, value1: item.number } - ), + title: actionCopy.title, description: nextState === 'closed' ? translate( @@ -3908,7 +3991,7 @@ function PRActionsPanel({ 'auto.components.GitHubItemDialog.b6f1b7adbd', 'This will reopen the pull request on GitHub.' ), - confirmLabel: label, + confirmLabel: actionCopy.confirmLabel, confirmVariant: nextState === 'closed' ? 'destructive' : 'default' }) if (!confirmed) { @@ -3935,13 +4018,7 @@ function PRActionsPanel({ onMutated() } catch (err) { applyStatePatch(previousState) - toast.error( - err instanceof Error - ? err.message - : translate('auto.components.GitHubItemDialog.e9b7cb7d17', 'Failed to {{value0}} PR', { - value0: label.toLowerCase() - }) - ) + toast.error(err instanceof Error ? err.message : actionCopy.failedMessage) } finally { setStatePending(false) } @@ -3951,18 +4028,14 @@ function PRActionsPanel({ if (mergeDisabled) { return } - const label = GITHUB_PR_MERGE_METHOD_LABELS[method] + const mergeMethodLabel = getLocalizedPullRequestMergeMethodLabel(method) const confirmed = await confirm({ - title: translate( - 'auto.components.GitHubItemDialog.03d7216d62', - '{{value0}} PR #{{value1}}?', - { value0: label, value1: item.number } - ), + title: getPullRequestMergeConfirmTitle(method, item.number), description: translate( 'auto.components.GitHubItemDialog.a27ee5ca1a', 'This will update the pull request on GitHub.' ), - confirmLabel: label + confirmLabel: mergeMethodLabel }) if (!confirmed) { return @@ -4115,7 +4188,7 @@ function PRActionsPanel({ )} {mergePresentation.autoMergeAction?.label ?? (mergePresentation.directMergeAvailable - ? mergeMethods.defaultLabel + ? getLocalizedPullRequestMergeMethodLabel(mergeMethods.defaultMethod) : mergePresentation.label)} @@ -4141,14 +4214,14 @@ function PRActionsPanel({ )} {mergePresentation.autoMergeAction && } - {mergeMethods.methods.map(({ method, label }) => ( + {mergeMethods.methods.map(({ method }) => ( void handleMerge(method)} > - {label} + {getLocalizedPullRequestMergeMethodLabel(method)} ))} window.api.shell.openUrl(item.url)}> @@ -4186,6 +4259,22 @@ function PRActionsPanel({ ) } +function getCommentReactionAriaLabel(reaction: GitHubReaction): string { + // Why: the translated sentence must own reaction/reactions for + // locale-specific pluralization and word order. + return reaction.count === 1 + ? translate( + 'auto.components.GitHubItemDialog.oneReactionAriaLabel', + '{{count}} {{reactionEmoji}} reaction', + { count: reaction.count, reactionEmoji: REACTION_EMOJI[reaction.content] } + ) + : translate( + 'auto.components.GitHubItemDialog.manyReactionsAriaLabel', + '{{count}} {{reactionEmoji}} reactions', + { count: reaction.count, reactionEmoji: REACTION_EMOJI[reaction.content] } + ) +} + function CommentReactions({ reactions }: { @@ -4202,15 +4291,7 @@ function CommentReactions({ {reaction.count} diff --git a/src/renderer/src/components/GitLabItemDialog.tsx b/src/renderer/src/components/GitLabItemDialog.tsx index b71386b4c34b..040facf0cb03 100644 --- a/src/renderer/src/components/GitLabItemDialog.tsx +++ b/src/renderer/src/components/GitLabItemDialog.tsx @@ -36,6 +36,7 @@ import { hasBoundedCommentBodyText } from '@/lib/comment-body-submit-state' import { useAppStore } from '@/store' +import { getGitLabApprovalSummary } from './gitlab-approval-summary' import type { GitLabAssignableUser, GitLabPipelineJob, @@ -1124,26 +1125,7 @@ export default function GitLabItemDialog({ {approvalState ? (
- {approvalState.approvalsLeft === 0 - ? translate( - 'auto.components.GitLabItemDialog.22511537d2', - 'Approved' - ) - : translate( - 'auto.components.GitLabItemDialog.40c56b95e2', - '{{value0}} approval{{value1}} remaining', - { - value0: approvalState.approvalsLeft ?? 0, - value1: approvalState.approvalsLeft === 1 ? '' : 's' - } - )} - {typeof approvalState.approvalsRequired === 'number' - ? translate( - 'auto.components.GitLabItemDialog.00f3bab87b', - ' of {{value0}} required', - { value0: approvalState.approvalsRequired } - ) - : ''} + {getGitLabApprovalSummary(approvalState)}
) : null} diff --git a/src/renderer/src/components/JiraIssueWorkspace.tsx b/src/renderer/src/components/JiraIssueWorkspace.tsx index 857df73e5994..8a278182aeb9 100644 --- a/src/renderer/src/components/JiraIssueWorkspace.tsx +++ b/src/renderer/src/components/JiraIssueWorkspace.tsx @@ -97,18 +97,19 @@ function jiraStatusClass(categoryKey: string): string { return 'border-border/50 bg-muted/40 text-muted-foreground' } -async function copyTextToClipboard(text: string, label: string): Promise { +// Why: this label is interpolated into localized toast sentences, so callers must localize it first. +async function copyTextToClipboard(text: string, localizedLabel: string): Promise { try { await window.api.ui.writeClipboardText(text) toast.success( translate('auto.components.JiraIssueWorkspace.2ff69a3545', '{{value0}} copied', { - value0: label + value0: localizedLabel }) ) } catch { toast.error( translate('auto.components.JiraIssueWorkspace.6c41a9bcea', 'Failed to copy {{value0}}', { - value0: label.toLowerCase() + value0: localizedLabel === 'URL' ? localizedLabel : localizedLabel.toLowerCase() }) ) } @@ -339,7 +340,10 @@ export default function JiraIssueWorkspace({ id: result.id || createBrowserUuid(), body: bodyState.body, createdAt: new Date().toISOString(), - user: { accountId: 'local', displayName: 'You' } + user: { + accountId: 'local', + displayName: translate('auto.components.JiraIssueWorkspace.localCommentAuthor', 'You') + } } optimisticCommentsRef.current.push(comment) setComments((prev) => [...prev, comment]) @@ -374,7 +378,11 @@ export default function JiraIssueWorkspace({ { label: translate('auto.components.JiraIssueWorkspace.38839801e8', 'Copy key'), icon: Clipboard, - action: () => void copyTextToClipboard(displayed.key, 'Key') + action: () => + void copyTextToClipboard( + displayed.key, + translate('auto.components.JiraIssueWorkspace.keyCopyLabel', 'Key') + ) }, { label: translate( @@ -382,12 +390,20 @@ export default function JiraIssueWorkspace({ 'Copy suggested branch name' ), icon: GitBranch, - action: () => void copyTextToClipboard(buildJiraBranchName(displayed), 'Branch name') + action: () => + void copyTextToClipboard( + buildJiraBranchName(displayed), + translate('auto.components.JiraIssueWorkspace.branchNameCopyLabel', 'Branch name') + ) }, { label: translate('auto.components.JiraIssueWorkspace.0cc62bd690', 'Copy prompt'), icon: Clipboard, - action: () => void copyTextToClipboard(buildJiraPrompt(displayed), 'Prompt') + action: () => + void copyTextToClipboard( + buildJiraPrompt(displayed), + translate('auto.components.JiraIssueWorkspace.promptCopyLabel', 'Prompt') + ) } ] }, [displayed]) diff --git a/src/renderer/src/components/LinearIssueWorkspace.tsx b/src/renderer/src/components/LinearIssueWorkspace.tsx index ccb1f03c0e54..dcdee2c3f234 100644 --- a/src/renderer/src/components/LinearIssueWorkspace.tsx +++ b/src/renderer/src/components/LinearIssueWorkspace.tsx @@ -71,18 +71,19 @@ type LinearIssueWorkspaceProps = { sourceContext?: TaskSourceContext | null } -async function copyTextToClipboard(text: string, label: string): Promise { +// Why: this label is interpolated into localized toast sentences, so callers must localize it first. +async function copyTextToClipboard(text: string, localizedLabel: string): Promise { try { await window.api.ui.writeClipboardText(text) toast.success( translate('auto.components.LinearIssueWorkspace.7835483c43', '{{value0}} copied', { - value0: label + value0: localizedLabel }) ) } catch { toast.error( translate('auto.components.LinearIssueWorkspace.9bcbaa2737', 'Failed to copy {{value0}}', { - value0: label.toLowerCase() + value0: localizedLabel === 'URL' ? localizedLabel : localizedLabel.toLowerCase() }) ) } @@ -659,7 +660,9 @@ export default function LinearIssueWorkspace({ id: comment.id || createBrowserUuid(), body: comment.body, createdAt: comment.createdAt, - user: { displayName: 'You' } + user: { + displayName: translate('auto.components.LinearIssueWorkspace.localCommentAuthor', 'You') + } } optimisticCommentsRef.current.push(newComment) setComments((prev) => [...prev, newComment]) @@ -682,7 +685,11 @@ export default function LinearIssueWorkspace({ { label: translate('auto.components.LinearIssueWorkspace.30c1242f3a', 'Copy identifier'), icon: Clipboard, - action: () => void copyTextToClipboard(displayed.identifier, 'Identifier') + action: () => + void copyTextToClipboard( + displayed.identifier, + translate('auto.components.LinearIssueWorkspace.identifierCopyLabel', 'Identifier') + ) }, { label: translate( @@ -691,7 +698,13 @@ export default function LinearIssueWorkspace({ ), icon: GitBranch, action: () => - void copyTextToClipboard(buildLinearIssueBranchName(displayed), 'Suggested branch name') + void copyTextToClipboard( + buildLinearIssueBranchName(displayed), + translate( + 'auto.components.LinearIssueWorkspace.suggestedBranchNameCopyLabel', + 'Suggested branch name' + ) + ) }, { label: translate('auto.components.LinearIssueWorkspace.f6c6381593', 'Copy prompt'), @@ -704,7 +717,10 @@ export default function LinearIssueWorkspace({ version: 1, renderedText }) ?? renderedText - void copyTextToClipboard(prompt, 'Prompt') + void copyTextToClipboard( + prompt, + translate('auto.components.LinearIssueWorkspace.promptCopyLabel', 'Prompt') + ) } } ] @@ -766,7 +782,15 @@ export default function LinearIssueWorkspace({ @@ -4129,14 +4202,14 @@ function PRActionsPanel({
)} {mergePresentation.autoMergeAction && } - {mergeMethods.methods.map(({ method, label }) => ( + {mergeMethods.methods.map(({ method }) => ( void handleMerge(method)} > - {label} + {getLocalizedPullRequestMergeMethodLabel(method)} ))} window.api.shell.openUrl(item.url)}> @@ -4174,6 +4247,22 @@ function PRActionsPanel({ ) } +function getCommentReactionAriaLabel(reaction: GitHubReaction): string { + // Why: the translated sentence must own reaction/reactions for + // locale-specific pluralization and word order. + return reaction.count === 1 + ? translate( + 'auto.components.PullRequestPage.oneReactionAriaLabel', + '{{count}} {{reactionEmoji}} reaction', + { count: reaction.count, reactionEmoji: REACTION_EMOJI[reaction.content] } + ) + : translate( + 'auto.components.PullRequestPage.manyReactionsAriaLabel', + '{{count}} {{reactionEmoji}} reactions', + { count: reaction.count, reactionEmoji: REACTION_EMOJI[reaction.content] } + ) +} + function CommentReactions({ reactions }: { @@ -4190,15 +4279,7 @@ function CommentReactions({ {reaction.count} diff --git a/src/renderer/src/components/TaskPage.tsx b/src/renderer/src/components/TaskPage.tsx index 99fc42d7111b..25b6a3856489 100644 --- a/src/renderer/src/components/TaskPage.tsx +++ b/src/renderer/src/components/TaskPage.tsx @@ -405,6 +405,38 @@ function getGitLabWorkItemWorkspaceSeed(item: GitLabWorkItem): string { ) } +function getSelectedLinearProjectMemberCountLabel(count: number): string { + // Why: member/members must remain part of the translated phrase for + // locale-specific pluralization. + return count === 1 + ? translate('auto.components.TaskPage.oneSelectedMember', '{{count}} member', { count }) + : translate('auto.components.TaskPage.manySelectedMembers', '{{count}} members', { count }) +} + +function getSelectedLinearLabelCountLabel(count: number): string { + // Why: project and issue selectors need the same complete, + // locale-orderable count phrase. + return count === 1 + ? translate('auto.components.TaskPage.oneSelectedLabel', '{{count}} label', { count }) + : translate('auto.components.TaskPage.manySelectedLabels', '{{count}} labels', { count }) +} + +function getGitLabStartWorkspaceAriaLabel(item: GitLabWorkItem): string { + // Why: issue is translated prose while MR is a product abbreviation, so + // the full branches stay distinct. + return item.type === 'mr' + ? translate( + 'auto.components.TaskPage.startWorkspaceFromGitLabMR', + 'Start workspace from MR {{number}}', + { number: item.number } + ) + : translate( + 'auto.components.TaskPage.startWorkspaceFromGitLabIssue', + 'Start workspace from issue {{number}}', + { number: item.number } + ) +} + function getJiraIssueWorkspaceSeed(issue: JiraIssue): string { return ( getLinkedWorkItemWorkspaceName({ @@ -10039,11 +10071,7 @@ export default function TaskPage(): React.JSX.Element { event.stopPropagation() handleUseGitLabItem(item) }} - aria-label={translate( - 'auto.components.TaskPage.5e8061b088', - 'Start workspace from {{value0}} {{value1}}', - { value0: item.type === 'mr' ? 'MR' : 'issue', value1: item.number } - )} + aria-label={getGitLabStartWorkspaceAriaLabel(item)} > @@ -11645,13 +11673,8 @@ export default function TaskPage(): React.JSX.Element { {newLinearProjectMemberIds.length === 0 ? translate('auto.components.TaskPage.d6cda23ef1', 'Members') - : translate( - 'auto.components.TaskPage.7719d8daa9', - '{{value0}} member{{value1}}', - { - value0: newLinearProjectMemberIds.length, - value1: newLinearProjectMemberIds.length > 1 ? 's' : '' - } + : getSelectedLinearProjectMemberCountLabel( + newLinearProjectMemberIds.length )} @@ -11719,14 +11742,7 @@ export default function TaskPage(): React.JSX.Element { {newLinearProjectLabelIds.length === 0 ? translate('auto.components.TaskPage.d0ca4aa1d0', 'Labels') - : translate( - 'auto.components.TaskPage.eff9800d4b', - '{{value0}} label{{value1}}', - { - value0: newLinearProjectLabelIds.length, - value1: newLinearProjectLabelIds.length > 1 ? 's' : '' - } - )} + : getSelectedLinearLabelCountLabel(newLinearProjectLabelIds.length)} @@ -12280,14 +12296,7 @@ export default function TaskPage(): React.JSX.Element { {newLinearIssueLabelIds.length === 0 ? translate('auto.components.TaskPage.d0ca4aa1d0', 'Labels') - : translate( - 'auto.components.TaskPage.eff9800d4b', - '{{value0}} label{{value1}}', - { - value0: newLinearIssueLabelIds.length, - value1: newLinearIssueLabelIds.length > 1 ? 's' : '' - } - )} + : getSelectedLinearLabelCountLabel(newLinearIssueLabelIds.length)} diff --git a/src/renderer/src/components/WorktreeJumpPalette.localization.test.ts b/src/renderer/src/components/WorktreeJumpPalette.localization.test.ts new file mode 100644 index 000000000000..64ef0af4f62f --- /dev/null +++ b/src/renderer/src/components/WorktreeJumpPalette.localization.test.ts @@ -0,0 +1,25 @@ +import { beforeEach, describe, expect, it } from 'vitest' + +import { i18n } from '@/i18n/i18n' +import { getWorktreeJumpPaletteAnnouncement } from './WorktreeJumpPalette' + +describe('WorktreeJumpPalette localized announcements', () => { + beforeEach(async () => { + await i18n.changeLanguage('en') + }) + + it.each([ + [true, 1, false, '1 result found'], + [true, 2, false, '2 results found'], + [true, 1, true, '1 result found; create worktree action available'], + [true, 2, true, '2 results found; create worktree action available'], + [false, 1, false, '1 item available'], + [false, 2, false, '2 items available'], + [false, 1, true, '1 item available; create worktree action available'], + [false, 2, true, '2 items available; create worktree action available'] + ])('formats query=%s count=%s create=%s', (hasQuery, resultCount, showCreateAction, expected) => { + expect(getWorktreeJumpPaletteAnnouncement({ hasQuery, resultCount, showCreateAction })).toBe( + expected + ) + }) +}) diff --git a/src/renderer/src/components/WorktreeJumpPalette.tsx b/src/renderer/src/components/WorktreeJumpPalette.tsx index 9b6fcef5b29d..6185ec450eca 100644 --- a/src/renderer/src/components/WorktreeJumpPalette.tsx +++ b/src/renderer/src/components/WorktreeJumpPalette.tsx @@ -327,6 +327,58 @@ function getSettingsTargetFromSectionId(sectionId: string): { return { pane: sectionId as SettingsNavTarget, repoId: null } } +export function getWorktreeJumpPaletteAnnouncement({ + hasQuery, + resultCount, + showCreateAction +}: { + hasQuery: boolean + resultCount: number + showCreateAction: boolean +}): string { + // Why: count grammar and the optional create action need complete localized sentences. + if (hasQuery) { + if (resultCount === 1) { + return showCreateAction + ? translate( + 'auto.components.WorktreeJumpPalette.oneResultFoundWithCreateAction', + '1 result found; create worktree action available' + ) + : translate('auto.components.WorktreeJumpPalette.oneResultFound', '1 result found') + } + return showCreateAction + ? translate( + 'auto.components.WorktreeJumpPalette.manyResultsFoundWithCreateAction', + '{{count}} results found; create worktree action available', + { count: resultCount } + ) + : translate( + 'auto.components.WorktreeJumpPalette.manyResultsFound', + '{{count}} results found', + { count: resultCount } + ) + } + if (resultCount === 1) { + return showCreateAction + ? translate( + 'auto.components.WorktreeJumpPalette.oneItemAvailableWithCreateAction', + '1 item available; create worktree action available' + ) + : translate('auto.components.WorktreeJumpPalette.oneItemAvailable', '1 item available') + } + return showCreateAction + ? translate( + 'auto.components.WorktreeJumpPalette.manyItemsAvailableWithCreateAction', + '{{count}} items available; create worktree action available', + { count: resultCount } + ) + : translate( + 'auto.components.WorktreeJumpPalette.manyItemsAvailable', + '{{count}} items available', + { count: resultCount } + ) +} + export default function WorktreeJumpPalette(): React.JSX.Element | null { // Why: subscribe this palette to language changes; translated memo contents // recompute on the rerender without using i18n.language as a fake dependency. @@ -2359,23 +2411,11 @@ export default function WorktreeJumpPalette(): React.JSX.Element | null {
- {deferredQuery.trim() - ? translate( - 'auto.components.WorktreeJumpPalette.bb72c08e63', - '{{value0}} results found{{value1}}', - { - value0: resultCount, - value1: showCreateAction ? ', create worktree action available' : '' - } - ) - : translate( - 'auto.components.WorktreeJumpPalette.20af998bff', - '{{value0}} items available{{value1}}', - { - value0: resultCount, - value1: showCreateAction ? ', create worktree action available' : '' - } - )} + {getWorktreeJumpPaletteAnnouncement({ + hasQuery: Boolean(deferredQuery.trim()), + resultCount, + showCreateAction + })}
) diff --git a/src/renderer/src/components/complete-localized-copy-boundary.test.ts b/src/renderer/src/components/complete-localized-copy-boundary.test.ts new file mode 100644 index 000000000000..76497049ebec --- /dev/null +++ b/src/renderer/src/components/complete-localized-copy-boundary.test.ts @@ -0,0 +1,261 @@ +import { readFileSync } from 'node:fs' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' + +const COMPONENT_ROOT = __dirname + +function componentSource(relativePath: string): string { + return readFileSync(join(COMPONENT_ROOT, relativePath), 'utf8') +} + +function expectTranslationCopy( + source: string, + entries: readonly { key: string; fallback: string }[] +): void { + for (const entry of entries) { + expect(source).toContain(`'${entry.key}'`) + expect(source).toContain(`'${entry.fallback}'`) + } +} + +describe('complete localized action and count copy', () => { + it.each([ + { + component: 'PullRequestPage.tsx', + namespace: 'auto.components.PullRequestPage', + oldViewedKey: 'auto.components.PullRequestPage.ff84e1f54c', + oldReactionKey: 'auto.components.PullRequestPage.42c36d9166', + oldActionTitleKey: 'auto.components.PullRequestPage.eec3706a6a', + oldActionFailedKey: 'auto.components.PullRequestPage.b8c6cbb8c4' + }, + { + component: 'GitHubItemDialog.tsx', + namespace: 'auto.components.GitHubItemDialog', + oldViewedKey: 'auto.components.GitHubItemDialog.2d89a38d9d', + oldReactionKey: 'auto.components.GitHubItemDialog.a18f669c7a', + oldActionTitleKey: 'auto.components.GitHubItemDialog.03d7216d62', + oldActionFailedKey: 'auto.components.GitHubItemDialog.e9b7cb7d17' + } + ])('keeps $component visible actions and reaction plurals in full sentences', (entry) => { + const source = componentSource(entry.component) + + expectTranslationCopy(source, [ + { + key: `${entry.namespace}.markFileAsViewed`, + fallback: 'Mark {{filePath}} as viewed' + }, + { + key: `${entry.namespace}.unmarkFileAsViewed`, + fallback: 'Unmark {{filePath}} as viewed' + }, + { + key: `${entry.namespace}.oneReactionAriaLabel`, + fallback: '{{count}} {{reactionEmoji}} reaction' + }, + { + key: `${entry.namespace}.manyReactionsAriaLabel`, + fallback: '{{count}} {{reactionEmoji}} reactions' + }, + { + key: `${entry.namespace}.confirmClosePullRequestTitle`, + fallback: 'Close PR #{{number}}?' + }, + { + key: `${entry.namespace}.closePullRequestConfirmLabel`, + fallback: 'Close' + }, + { + key: `${entry.namespace}.failedToClosePullRequest`, + fallback: 'Failed to close PR' + }, + { + key: `${entry.namespace}.confirmReopenPullRequestTitle`, + fallback: 'Reopen PR #{{number}}?' + }, + { + key: `${entry.namespace}.reopenPullRequestConfirmLabel`, + fallback: 'Reopen' + }, + { + key: `${entry.namespace}.failedToReopenPullRequest`, + fallback: 'Failed to reopen PR' + }, + { + key: `${entry.namespace}.squashAndMergeLabel`, + fallback: 'Squash and merge' + }, + { + key: `${entry.namespace}.createMergeCommitLabel`, + fallback: 'Create merge commit' + }, + { + key: `${entry.namespace}.rebaseAndMergeLabel`, + fallback: 'Rebase and merge' + }, + { + key: `${entry.namespace}.confirmSquashAndMergeTitle`, + fallback: 'Squash and merge PR #{{number}}?' + }, + { + key: `${entry.namespace}.confirmCreateMergeCommitTitle`, + fallback: 'Create merge commit for PR #{{number}}?' + }, + { + key: `${entry.namespace}.confirmRebaseAndMergeTitle`, + fallback: 'Rebase and merge PR #{{number}}?' + } + ]) + expect(source).not.toContain(entry.oldViewedKey) + expect(source).not.toContain(entry.oldReactionKey) + expect(source).not.toContain(entry.oldActionTitleKey) + expect(source).not.toContain(entry.oldActionFailedKey) + expect(source).not.toContain('{{value0}} {{value1}} as viewed') + expect(source).not.toContain('reaction{{value2}}') + expect(source).not.toContain('reaction: reaction.content') + expect(source).toContain('reactionEmoji: REACTION_EMOJI[reaction.content]') + expect(source).not.toContain('GITHUB_PR_MERGE_METHOD_LABELS') + expect(source).not.toContain("const label = nextState === 'closed' ? 'Close' : 'Reopen'") + }) + + it('uses complete shared member and label count phrases in TaskPage', () => { + const source = componentSource('TaskPage.tsx') + + expectTranslationCopy(source, [ + { + key: 'auto.components.TaskPage.oneSelectedMember', + fallback: '{{count}} member' + }, + { + key: 'auto.components.TaskPage.manySelectedMembers', + fallback: '{{count}} members' + }, + { + key: 'auto.components.TaskPage.oneSelectedLabel', + fallback: '{{count}} label' + }, + { + key: 'auto.components.TaskPage.manySelectedLabels', + fallback: '{{count}} labels' + } + ]) + expect(source.match(/getSelectedLinearProjectMemberCountLabel\(/g)).toHaveLength(2) + expect(source.match(/getSelectedLinearLabelCountLabel\(/g)).toHaveLength(3) + expect(source).not.toContain('auto.components.TaskPage.7719d8daa9') + expect(source).not.toContain('auto.components.TaskPage.eff9800d4b') + expect(source).not.toContain('member{{value1}}') + expect(source).not.toContain('label{{value1}}') + }) + + it('keeps the GitLab MR abbreviation and translated issue noun in separate TaskPage keys', () => { + const source = componentSource('TaskPage.tsx') + + expectTranslationCopy(source, [ + { + key: 'auto.components.TaskPage.startWorkspaceFromGitLabMR', + fallback: 'Start workspace from MR {{number}}' + }, + { + key: 'auto.components.TaskPage.startWorkspaceFromGitLabIssue', + fallback: 'Start workspace from issue {{number}}' + } + ]) + expect(source.match(/getGitLabStartWorkspaceAriaLabel\(/g)).toHaveLength(2) + expect(source).not.toContain('auto.components.TaskPage.5e8061b088') + expect(source).not.toContain('Start workspace from {{value0}} {{value1}}') + }) + + it('keeps highlighted worktree examples inside complete translatable sentences', () => { + const source = componentSource(join('feature-tips', 'FeatureTipsModal.tsx')) + + expect(source).toContain( + 'translationKey="auto.components.feature.tips.FeatureTipsModal.splitWorktreesPrompt"' + ) + expect(source).toContain( + 'fallback="“Split this PR into two {{term}} and create PRs for each.”"' + ) + expect(source).toContain( + 'translationKey="auto.components.feature.tips.FeatureTipsModal.handoffReviewPrompt"' + ) + expect(source).toContain( + 'fallback="“When the agent in {{term}} X finishes, send it the review task.”"' + ) + expect(source).not.toContain('auto.components.feature.tips.FeatureTipsModal.55846c7f95') + expect(source).not.toContain('auto.components.feature.tips.FeatureTipsModal.7fc6f02099') + expect(source).not.toContain('auto.components.feature.tips.FeatureTipsModal.864e2db28f') + expect(source).not.toContain('auto.components.feature.tips.FeatureTipsModal.3c6c478462') + }) + + it('uses complete discard-error and note-count phrases in SourceControl', () => { + const source = componentSource(join('right-sidebar', 'SourceControl.tsx')) + + expectTranslationCopy(source, [ + { + key: 'auto.components.right.sidebar.SourceControl.failedToDiscardOneFile', + fallback: 'Failed to discard {{count}} file' + }, + { + key: 'auto.components.right.sidebar.SourceControl.failedToDiscardManyFiles', + fallback: 'Failed to discard {{count}} files' + }, + { + key: 'auto.components.right.sidebar.SourceControl.oneNote', + fallback: '{{count}} note' + }, + { + key: 'auto.components.right.sidebar.SourceControl.manyNotes', + fallback: '{{count}} notes' + }, + { + key: 'auto.components.right.sidebar.SourceControl.abortRebaseTitle', + fallback: 'Abort rebase?' + }, + { + key: 'auto.components.right.sidebar.SourceControl.abortRebaseDescription', + fallback: + 'This cancels the rebase in progress and can discard conflict resolutions made during this rebase.' + }, + { + key: 'auto.components.right.sidebar.SourceControl.abortRebaseConfirmLabel', + fallback: 'Abort rebase' + }, + { + key: 'auto.components.right.sidebar.SourceControl.abortRebaseFailed', + fallback: 'Abort rebase failed' + }, + { + key: 'auto.components.right.sidebar.SourceControl.failedToAbortRebase', + fallback: 'Failed to abort rebase' + }, + { + key: 'auto.components.right.sidebar.SourceControl.abortMergeTitle', + fallback: 'Abort merge?' + }, + { + key: 'auto.components.right.sidebar.SourceControl.abortMergeDescription', + fallback: + 'This cancels the merge in progress and can discard conflict resolutions made during this merge.' + }, + { + key: 'auto.components.right.sidebar.SourceControl.abortMergeConfirmLabel', + fallback: 'Abort merge' + }, + { + key: 'auto.components.right.sidebar.SourceControl.abortMergeFailed', + fallback: 'Abort merge failed' + }, + { + key: 'auto.components.right.sidebar.SourceControl.failedToAbortMerge', + fallback: 'Failed to abort merge' + } + ]) + expect(source.match(/getFailedDiscardFileCountLabel\(/g)).toHaveLength(2) + expect(source.match(/getDiffNoteCountLabel\(/g)).toHaveLength(3) + expect(source).not.toContain('auto.components.right.sidebar.SourceControl.8eb3782a0c') + expect(source).not.toContain('auto.components.right.sidebar.SourceControl.657e0c90ad') + expect(source).not.toContain('auto.components.right.sidebar.SourceControl.f99560ab29') + expect(source).not.toContain('file{{value1}}') + expect(source).not.toContain('note{{value1}}') + expect(source).not.toContain('Abort {{value0}} failed') + expect(source).not.toContain('`Abort ${label}`') + }) +}) diff --git a/src/renderer/src/components/dashboard/DashboardAgentChildDisclosure.tsx b/src/renderer/src/components/dashboard/DashboardAgentChildDisclosure.tsx index 82cd87fe47c0..00728d426c83 100644 --- a/src/renderer/src/components/dashboard/DashboardAgentChildDisclosure.tsx +++ b/src/renderer/src/components/dashboard/DashboardAgentChildDisclosure.tsx @@ -43,6 +43,31 @@ export function DashboardAgentChildDisclosure({ ) : null } + // Why: translating the full action avoids leaking English verbs and plural nouns into CJK labels. + const disclosureLabel = childAgentsExpanded + ? childAgentCount === 1 + ? translate( + 'auto.components.dashboard.DashboardAgentChildDisclosure.hideOneChildAgent', + 'Hide {{count}} child agent', + { count: childAgentCount } + ) + : translate( + 'auto.components.dashboard.DashboardAgentChildDisclosure.hideManyChildAgents', + 'Hide {{count}} child agents', + { count: childAgentCount } + ) + : childAgentCount === 1 + ? translate( + 'auto.components.dashboard.DashboardAgentChildDisclosure.showOneChildAgent', + 'Show {{count}} child agent', + { count: childAgentCount } + ) + : translate( + 'auto.components.dashboard.DashboardAgentChildDisclosure.showManyChildAgents', + 'Show {{count}} child agents', + { count: childAgentCount } + ) + // Why: the chevron owns child disclosure; leaf spacers keep the leading // state-dot column aligned across the card. return ( @@ -52,15 +77,7 @@ export function DashboardAgentChildDisclosure({ onMouseDown={stopMouseDown} onKeyDown={stopKeyDown} className="-ml-0.5 inline-flex size-4 shrink-0 items-center justify-center rounded-sm border border-sidebar-border/80 bg-sidebar text-foreground/80 shadow-xs hover:bg-sidebar-accent hover:text-foreground" - aria-label={translate( - 'auto.components.dashboard.DashboardAgentChildDisclosure.1b57ce9fa4', - '{{value0}} {{value1}} child {{value2}}', - { - value0: childAgentsExpanded ? 'Hide' : 'Show', - value1: childAgentCount, - value2: childAgentCount === 1 ? 'agent' : 'agents' - } - )} + aria-label={disclosureLabel} aria-expanded={childAgentsExpanded} > diff --git a/src/renderer/src/components/editor/ConflictComponents.test.tsx b/src/renderer/src/components/editor/ConflictComponents.test.tsx index 0b5e91e9d945..844c3a89b961 100644 --- a/src/renderer/src/components/editor/ConflictComponents.test.tsx +++ b/src/renderer/src/components/editor/ConflictComponents.test.tsx @@ -1,5 +1,10 @@ import { describe, expect, it } from 'vitest' -import { getNextConflictNavigationIndex } from './ConflictComponents' +import { + getConflictHint, + getConflictKindLabel, + getConflictStatusLabel, + getNextConflictNavigationIndex +} from './ConflictComponents' describe('getNextConflictNavigationIndex', () => { it('cycles through conflicts in both directions', () => { @@ -18,3 +23,32 @@ describe('getNextConflictNavigationIndex', () => { ) }) }) + +describe('localized conflict copy', () => { + it('resolves every conflict kind to its own label and hint', () => { + expect(getConflictKindLabel('both_modified')).toBe('Both modified') + expect(getConflictKindLabel('both_added')).toBe('Both added') + expect(getConflictKindLabel('deleted_by_us')).toBe('Deleted by us') + expect(getConflictKindLabel('deleted_by_them')).toBe('Deleted by them') + expect(getConflictKindLabel('added_by_us')).toBe('Added by us') + expect(getConflictKindLabel('added_by_them')).toBe('Added by them') + expect(getConflictKindLabel('both_deleted')).toBe('Both deleted') + + expect(getConflictHint('both_modified')).toBe('Resolve the conflict markers') + expect(getConflictHint('both_added')).toBe('Choose which version to keep, or combine them') + expect(getConflictHint('deleted_by_us')).toBe('Decide whether to restore the file') + expect(getConflictHint('deleted_by_them')).toBe( + 'Decide whether to keep the file or accept deletion' + ) + expect(getConflictHint('added_by_us')).toBe('Review whether to keep the added file') + expect(getConflictHint('added_by_them')).toBe('Review the added file before keeping it') + expect(getConflictHint('both_deleted')).toBe( + 'Resolve in Git or restore one side before editing' + ) + }) + + it('keeps unresolved and locally resolved states as separate translation units', () => { + expect(getConflictStatusLabel(true)).toBe('Unresolved') + expect(getConflictStatusLabel(false)).toBe('Resolved locally') + }) +}) diff --git a/src/renderer/src/components/editor/ConflictComponents.tsx b/src/renderer/src/components/editor/ConflictComponents.tsx index 47c74ae88db3..b72a3ad58ac3 100644 --- a/src/renderer/src/components/editor/ConflictComponents.tsx +++ b/src/renderer/src/components/editor/ConflictComponents.tsx @@ -17,24 +17,74 @@ import type { GitConflictKind, GitStatusEntry } from '../../../../shared/types' import { ConflictReviewFileTree } from './ConflictReviewFileTree' import { translate } from '@/i18n/i18n' -export const CONFLICT_KIND_LABELS: Record = { - both_modified: 'Both modified', - both_added: 'Both added', - deleted_by_us: 'Deleted by us', - deleted_by_them: 'Deleted by them', - added_by_us: 'Added by us', - added_by_them: 'Added by them', - both_deleted: 'Both deleted' +// Why: every Git conflict kind is a distinct translation concept, and resolving +// it on demand keeps these exported accessors in sync with runtime locale changes. +const CONFLICT_KIND_LABEL_RESOLVERS: Record string> = { + both_modified: () => + translate('auto.components.editor.ConflictComponents.71a2c63628', 'Both modified'), + both_added: () => translate('auto.components.editor.ConflictComponents.ba602cdc05', 'Both added'), + deleted_by_us: () => + translate('auto.components.editor.ConflictComponents.1c55f94973', 'Deleted by us'), + deleted_by_them: () => + translate('auto.components.editor.ConflictComponents.9d8757e1ca', 'Deleted by them'), + added_by_us: () => + translate('auto.components.editor.ConflictComponents.36bceb6bb5', 'Added by us'), + added_by_them: () => + translate('auto.components.editor.ConflictComponents.5259d6908a', 'Added by them'), + both_deleted: () => + translate('auto.components.editor.ConflictComponents.be2b3274fe', 'Both deleted') } -export const CONFLICT_HINT_MAP: Record = { - both_modified: 'Resolve the conflict markers', - both_added: 'Choose which version to keep, or combine them', - deleted_by_us: 'Decide whether to restore the file', - deleted_by_them: 'Decide whether to keep the file or accept deletion', - added_by_us: 'Review whether to keep the added file', - added_by_them: 'Review the added file before keeping it', - both_deleted: 'Resolve in Git or restore one side before editing' +const CONFLICT_HINT_RESOLVERS: Record string> = { + both_modified: () => + translate( + 'auto.components.editor.ConflictComponents.b119865c75', + 'Resolve the conflict markers' + ), + both_added: () => + translate( + 'auto.components.editor.ConflictComponents.5e89c1d824', + 'Choose which version to keep, or combine them' + ), + deleted_by_us: () => + translate( + 'auto.components.editor.ConflictComponents.7e6a9cd8ba', + 'Decide whether to restore the file' + ), + deleted_by_them: () => + translate( + 'auto.components.editor.ConflictComponents.9d73608d56', + 'Decide whether to keep the file or accept deletion' + ), + added_by_us: () => + translate( + 'auto.components.editor.ConflictComponents.2ff72311cd', + 'Review whether to keep the added file' + ), + added_by_them: () => + translate( + 'auto.components.editor.ConflictComponents.7e15338be9', + 'Review the added file before keeping it' + ), + both_deleted: () => + translate( + 'auto.components.editor.ConflictComponents.a63bf70fc7', + 'Resolve in Git or restore one side before editing' + ) +} + +export function getConflictKindLabel(kind: GitConflictKind): string { + return CONFLICT_KIND_LABEL_RESOLVERS[kind]() +} + +export function getConflictHint(kind: GitConflictKind): string { + return CONFLICT_HINT_RESOLVERS[kind]() +} + +export function getConflictStatusLabel(isUnresolved: boolean): string { + return isUnresolved + ? translate('auto.components.editor.ConflictComponents.ec7ef38d67', 'Unresolved') + : translate('auto.components.editor.ConflictComponents.485ab716f0', 'Resolved locally') } const EMPTY_CONFLICT_REVIEW_ENTRIES: readonly ConflictReviewEntry[] = [] @@ -81,7 +131,7 @@ export function ConflictBanner({ } const isUnresolved = conflict.conflictStatus === 'unresolved' - const label = isUnresolved ? 'Unresolved' : 'Resolved locally' + const label = getConflictStatusLabel(isUnresolved) return (
{label}{' '} {translate('auto.components.editor.ConflictComponents.55d61a0ccd', 'conflict ·')} - {CONFLICT_KIND_LABELS[conflict.conflictKind]} + {getConflictKindLabel(conflict.conflictKind)} {conflictNavigation && conflictNavigation.total > 0 && ( @@ -189,7 +239,7 @@ export function ConflictPlaceholderView({ file }: { file: OpenFile }): React.JSX
- {CONFLICT_KIND_LABELS[conflict.conflictKind]} + {getConflictKindLabel(conflict.conflictKind)}
{conflict.message ?? @@ -199,7 +249,7 @@ export function ConflictPlaceholderView({ file }: { file: OpenFile }): React.JSX )}
- {conflict.guidance ?? CONFLICT_HINT_MAP[conflict.conflictKind]} + {conflict.guidance ?? getConflictHint(conflict.conflictKind)}
diff --git a/src/renderer/src/components/feature-tips/FeatureTipsModal.tsx b/src/renderer/src/components/feature-tips/FeatureTipsModal.tsx index dd4a647a1457..c66503c161a6 100644 --- a/src/renderer/src/components/feature-tips/FeatureTipsModal.tsx +++ b/src/renderer/src/components/feature-tips/FeatureTipsModal.tsx @@ -44,6 +44,35 @@ function WorktreePromptTerm({ children }: { children: string }): JSX.Element { ) } +const PROMPT_TERM_MARKER = '\u0000orca-worktree-term\u0000' + +function LocalizedWorktreePrompt({ + translationKey, + fallback, + termKey, + termFallback +}: { + translationKey: string + fallback: string + termKey: string + termFallback: string +}): JSX.Element { + // Why: complete-sentence translation lets CJK locales reorder the highlighted worktree term safely. + const copy = translate(translationKey, fallback, { term: PROMPT_TERM_MARKER }) + const markerIndex = copy.indexOf(PROMPT_TERM_MARKER) + if (markerIndex < 0) { + return <>{copy} + } + + return ( + <> + {copy.slice(0, markerIndex)} + {translate(termKey, termFallback)} + {copy.slice(markerIndex + PROMPT_TERM_MARKER.length)} + + ) +} + function FeatureTipVisual({ tip }: { tip: FeatureTip }): JSX.Element { if (tip.action === 'setup-cli') { return @@ -306,36 +335,20 @@ export default function FeatureTipsModal(): JSX.Element | null { )}

- {translate( - 'auto.components.feature.tips.FeatureTipsModal.55846c7f95', - '“Split this PR into two' - )} - - {translate( - 'auto.components.feature.tips.FeatureTipsModal.27c567a89c', - 'worktrees' - )} - {' '} - {translate( - 'auto.components.feature.tips.FeatureTipsModal.7fc6f02099', - 'and create PRs for each.”' - )} +

- {translate( - 'auto.components.feature.tips.FeatureTipsModal.864e2db28f', - '“When the agent in' - )} - - {translate( - 'auto.components.feature.tips.FeatureTipsModal.298301b7a0', - 'worktree' - )} - {' '} - {translate( - 'auto.components.feature.tips.FeatureTipsModal.3c6c478462', - 'X finishes, send it the review task.”' - )} +

diff --git a/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx b/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx index 0db0de4966c9..45e91db9114b 100644 --- a/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx +++ b/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx @@ -89,6 +89,18 @@ vi.mock('./FloatingTerminalIconContextMenu', () => ({ })) const storeState = vi.hoisted(() => ({ hasFloatingUnread: false })) +const translateMock = vi.hoisted(() => + vi.fn((_key: string, fallback: string, values?: Record) => + Object.entries(values ?? {}).reduce( + (message, [name, value]) => message.replaceAll(`{{${name}}}`, String(value)), + fallback + ) + ) +) + +vi.mock('@/i18n/i18n', () => ({ + translate: translateMock +})) vi.mock('@/store', () => ({ useAppStore: (selector: (state: typeof storeState) => T): T => selector(storeState) @@ -139,6 +151,19 @@ function hasProp(node: unknown, propName: string): boolean { return found } +function collectText(node: unknown): string { + if (typeof node === 'string' || typeof node === 'number') { + return String(node) + } + if (node == null) { + return '' + } + if (Array.isArray(node)) { + return node.map(collectText).join('') + } + return collectText((node as ReactElementLike).props?.children) +} + function runEffects(): void { const layoutEffects = hookRuntime.layoutEffects.splice(0) for (const effect of layoutEffects) { @@ -294,4 +319,22 @@ describe('FloatingTerminalToggleButton attention dot', () => { const element = await renderToggle(false) expect(hasProp(element, 'data-floating-terminal-attention')).toBe(false) }) + + it('uses complete show and minimize tooltip translations', async () => { + const closedElement = await renderToggle(false) + const openElement = await renderToggle(true) + + expect(collectText(closedElement)).toContain('Show floating workspace (Cmd+`)') + expect(collectText(openElement)).toContain('Minimize floating workspace (Cmd+`)') + expect(translateMock).toHaveBeenCalledWith( + 'auto.components.floating.terminal.FloatingTerminalToggleButton.showFloatingWorkspaceWithShortcut', + 'Show floating workspace ({{shortcut}})', + { shortcut: 'Cmd+`' } + ) + expect(translateMock).toHaveBeenCalledWith( + 'auto.components.floating.terminal.FloatingTerminalToggleButton.minimizeFloatingWorkspaceWithShortcut', + 'Minimize floating workspace ({{shortcut}})', + { shortcut: 'Cmd+`' } + ) + }) }) diff --git a/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.tsx b/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.tsx index 285ff037778f..2e0a955622d8 100644 --- a/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.tsx +++ b/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.tsx @@ -64,6 +64,19 @@ export function FloatingTerminalToggleButton({ onToggle: () => void }): React.JSX.Element { const shortcutLabel = useShortcutLabel('floatingTerminal.toggle') + // Why: the action belongs inside the complete tooltip translation so it + // can move with the sentence instead of being injected as an English verb. + const tooltipLabel = open + ? translate( + 'auto.components.floating.terminal.FloatingTerminalToggleButton.minimizeFloatingWorkspaceWithShortcut', + 'Minimize floating workspace ({{shortcut}})', + { shortcut: shortcutLabel } + ) + : translate( + 'auto.components.floating.terminal.FloatingTerminalToggleButton.showFloatingWorkspaceWithShortcut', + 'Show floating workspace ({{shortcut}})', + { shortcut: shortcutLabel } + ) // Why: show an attention dot while minimized (closed) when any floating- // workspace tab still has an unacknowledged bell or agent completion. Derived // from the shared unread maps, so it clears when the user engages with — or @@ -249,11 +262,7 @@ export function FloatingTerminalToggleButton({
- {translate( - 'auto.components.floating.terminal.FloatingTerminalToggleButton.bfe7809a70', - '{{value0}} floating workspace ({{value1}})', - { value0: open ? 'Minimize' : 'Show', value1: shortcutLabel } - )} + {tooltipLabel}
diff --git a/src/renderer/src/components/gitlab-approval-summary.test.ts b/src/renderer/src/components/gitlab-approval-summary.test.ts new file mode 100644 index 000000000000..fa15c65dd21c --- /dev/null +++ b/src/renderer/src/components/gitlab-approval-summary.test.ts @@ -0,0 +1,62 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { getGitLabApprovalSummary } from './gitlab-approval-summary' + +const mocks = vi.hoisted(() => ({ + translate: vi.fn((_key: string, fallback: string) => fallback) +})) + +vi.mock('@/i18n/i18n', () => ({ + translate: mocks.translate +})) + +describe('getGitLabApprovalSummary', () => { + beforeEach(() => { + mocks.translate.mockClear() + }) + + it('preserves the required count when all approvals are satisfied', () => { + getGitLabApprovalSummary({ approvalsLeft: 0, approvalsRequired: 3 }) + + expect(mocks.translate).toHaveBeenCalledWith( + 'auto.components.GitLabItemDialog.approvedWithRequirement', + 'Approved · {{requiredCount}} required', + { requiredCount: 3 } + ) + }) + + it('uses complete singular and plural messages when a requirement is known', () => { + getGitLabApprovalSummary({ approvalsLeft: 1, approvalsRequired: 3 }) + getGitLabApprovalSummary({ approvalsLeft: 2, approvalsRequired: 3 }) + + expect(mocks.translate).toHaveBeenNthCalledWith( + 1, + 'auto.components.GitLabItemDialog.oneApprovalRemainingOfRequired', + '{{remainingCount}} approval remaining of {{requiredCount}} required', + { remainingCount: 1, requiredCount: 3 } + ) + expect(mocks.translate).toHaveBeenNthCalledWith( + 2, + 'auto.components.GitLabItemDialog.manyApprovalsRemainingOfRequired', + '{{remainingCount}} approvals remaining of {{requiredCount}} required', + { remainingCount: 2, requiredCount: 3 } + ) + }) + + it('uses complete messages without appending a translated requirement fragment', () => { + getGitLabApprovalSummary({ approvalsLeft: 1, approvalsRequired: null }) + getGitLabApprovalSummary({ approvalsLeft: 4, approvalsRequired: null }) + + expect(mocks.translate).toHaveBeenNthCalledWith( + 1, + 'auto.components.GitLabItemDialog.oneApprovalRemaining', + '{{remainingCount}} approval remaining', + { remainingCount: 1 } + ) + expect(mocks.translate).toHaveBeenNthCalledWith( + 2, + 'auto.components.GitLabItemDialog.manyApprovalsRemaining', + '{{remainingCount}} approvals remaining', + { remainingCount: 4 } + ) + }) +}) diff --git a/src/renderer/src/components/gitlab-approval-summary.ts b/src/renderer/src/components/gitlab-approval-summary.ts new file mode 100644 index 000000000000..95b62bd7b1a2 --- /dev/null +++ b/src/renderer/src/components/gitlab-approval-summary.ts @@ -0,0 +1,48 @@ +import { translate } from '@/i18n/i18n' + +type GitLabApprovalSummary = { + approvalsLeft: number | null + approvalsRequired: number | null +} + +export function getGitLabApprovalSummary(state: GitLabApprovalSummary): string { + if (state.approvalsLeft === 0) { + if (typeof state.approvalsRequired === 'number') { + return translate( + 'auto.components.GitLabItemDialog.approvedWithRequirement', + 'Approved · {{requiredCount}} required', + { requiredCount: state.approvalsRequired } + ) + } + return translate('auto.components.GitLabItemDialog.22511537d2', 'Approved') + } + + // Why: approval count and requirement form one translatable sentence; + // concatenated English suffixes and fragments prevent natural word order. + const remainingCount = state.approvalsLeft ?? 0 + if (typeof state.approvalsRequired === 'number') { + return remainingCount === 1 + ? translate( + 'auto.components.GitLabItemDialog.oneApprovalRemainingOfRequired', + '{{remainingCount}} approval remaining of {{requiredCount}} required', + { remainingCount, requiredCount: state.approvalsRequired } + ) + : translate( + 'auto.components.GitLabItemDialog.manyApprovalsRemainingOfRequired', + '{{remainingCount}} approvals remaining of {{requiredCount}} required', + { remainingCount, requiredCount: state.approvalsRequired } + ) + } + + return remainingCount === 1 + ? translate( + 'auto.components.GitLabItemDialog.oneApprovalRemaining', + '{{remainingCount}} approval remaining', + { remainingCount } + ) + : translate( + 'auto.components.GitLabItemDialog.manyApprovalsRemaining', + '{{remainingCount}} approvals remaining', + { remainingCount } + ) +} diff --git a/src/renderer/src/components/linear-project-view-surfaces.tsx b/src/renderer/src/components/linear-project-view-surfaces.tsx index 58970a8215a8..e16d5211c7f3 100644 --- a/src/renderer/src/components/linear-project-view-surfaces.tsx +++ b/src/renderer/src/components/linear-project-view-surfaces.tsx @@ -399,7 +399,7 @@ export function LinearProjectTable({ onOpenProject(project) }} aria-label={translate( - 'auto.components.linear.project.view.surfaces.7616c986c6', + 'auto.components.linear.project.view.surfaces.openInLinear', 'Open {{value0}} in Linear', { value0: project.name } )} @@ -549,7 +549,7 @@ export function LinearCustomViewTable({ onOpenView(view) }} aria-label={translate( - 'auto.components.linear.project.view.surfaces.7616c986c6', + 'auto.components.linear.project.view.surfaces.openInLinear', 'Open {{value0}} in Linear', { value0: view.name } )} diff --git a/src/renderer/src/components/onboarding/IntegrationsStep.tsx b/src/renderer/src/components/onboarding/IntegrationsStep.tsx index 161ea88a9f51..db5b7350d04f 100644 --- a/src/renderer/src/components/onboarding/IntegrationsStep.tsx +++ b/src/renderer/src/components/onboarding/IntegrationsStep.tsx @@ -7,6 +7,7 @@ import { useAppStore } from '@/store' import { IntegrationStatusPill } from '@/components/integration-status-pill' import { cn } from '@/lib/utils' import { OnboardingInlineCommandTerminal } from './OnboardingInlineCommandTerminal' +import { getLinearWorkspaceLinkSummary } from './linear-workspace-link-summary' import { translate } from '@/i18n/i18n' type GitHubSetupState = 'checking' | 'connected' | 'not-installed' | 'not-authenticated' @@ -169,11 +170,7 @@ export function LinearRow(props: { compact?: boolean } = {}): React.JSX.Element

{linearStatus.connected - ? translate( - 'auto.components.onboarding.IntegrationsStep.b08a6ac93c', - '{{value0}} workspace{{value1}} linked. Add another workspace or replace a restricted key any time.', - { value0: workspaceCount, value1: workspaceCount === 1 ? '' : 's' } - ) + ? getLinearWorkspaceLinkSummary(workspaceCount) : translate( 'auto.components.onboarding.IntegrationsStep.4983ae7433', 'Add Linear access with a Personal API key. Full-access keys can show every team the key owner can access.' diff --git a/src/renderer/src/components/onboarding/RepoStep.test.tsx b/src/renderer/src/components/onboarding/RepoStep.test.tsx index de01325e1d51..e734936362e5 100644 --- a/src/renderer/src/components/onboarding/RepoStep.test.tsx +++ b/src/renderer/src/components/onboarding/RepoStep.test.tsx @@ -105,6 +105,30 @@ describe('RepoStep', () => { expect(html.match(/disabled=""/g)?.length).toBeGreaterThanOrEqual(3) }) + it('uses a complete plural sentence while a nested scan is still running', () => { + const html = renderRepoStep({ + nestedScan: { + selectedPath: '/workspace/platform', + selectedPathKind: 'non_git_folder', + repos: [ + { path: '/workspace/platform/apps/web', displayName: 'web', depth: 2 }, + { path: '/workspace/platform/apps/api', displayName: 'api', depth: 2 } + ], + truncated: false, + timedOut: false, + stopped: false, + durationMs: 4, + maxDepth: 3, + maxRepos: 100, + timeoutMs: null + }, + nestedScanInProgress: true, + nestedSelectedPaths: new Set(['/workspace/platform/apps/web']) + }) + + expect(html).toContain('Scanning... Found 2 repositories in this folder.') + }) + it('shows a flat nested repository checklist with collision labels', () => { const html = renderRepoStep({ nestedScan: { diff --git a/src/renderer/src/components/onboarding/RepoStepNestedImportPanel.tsx b/src/renderer/src/components/onboarding/RepoStepNestedImportPanel.tsx index e7b24e2b71ef..55a1fb9a573f 100644 --- a/src/renderer/src/components/onboarding/RepoStepNestedImportPanel.tsx +++ b/src/renderer/src/components/onboarding/RepoStepNestedImportPanel.tsx @@ -35,6 +35,32 @@ export function RepoStepNestedImportPanel({ }: RepoStepNestedImportPanelProps) { const folderName = getRuntimePathBasename(nestedScan.selectedPath) || nestedScan.selectedPath const nestedImportDisabled = disabled || nestedScanInProgress + const repositoryCount = nestedScan.repos.length + // Why: scan state and noun form belong to the full translated sentence; + // interpolated English fragments prevent natural word order. + const scanSummary = nestedScanInProgress + ? repositoryCount === 1 + ? translate( + 'auto.components.onboarding.RepoStepNestedImportPanel.scanningFoundOneRepository', + 'Scanning... Found {{count}} repository in this folder.', + { count: repositoryCount } + ) + : translate( + 'auto.components.onboarding.RepoStepNestedImportPanel.scanningFoundManyRepositories', + 'Scanning... Found {{count}} repositories in this folder.', + { count: repositoryCount } + ) + : repositoryCount === 1 + ? translate( + 'auto.components.onboarding.RepoStepNestedImportPanel.foundOneRepository', + 'Found {{count}} repository in this folder.', + { count: repositoryCount } + ) + : translate( + 'auto.components.onboarding.RepoStepNestedImportPanel.foundManyRepositories', + 'Found {{count}} repositories in this folder.', + { count: repositoryCount } + ) return (

@@ -77,17 +103,7 @@ export function RepoStepNestedImportPanel({ ) : null} - - {translate( - 'auto.components.onboarding.RepoStep.2e6438dd34', - '{{value0}}Found {{value1}} {{value2}} in this folder.', - { - value0: nestedScanInProgress ? 'Scanning... ' : '', - value1: nestedScan.repos.length, - value2: nestedScan.repos.length === 1 ? 'repository' : 'repositories' - } - )} - + {scanSummary}
{translate('auto.components.onboarding.RepoStep.cecd6593fa', 'Scanned folder:')}{' '} diff --git a/src/renderer/src/components/onboarding/linear-workspace-link-summary.test.ts b/src/renderer/src/components/onboarding/linear-workspace-link-summary.test.ts new file mode 100644 index 000000000000..ac6290073cf4 --- /dev/null +++ b/src/renderer/src/components/onboarding/linear-workspace-link-summary.test.ts @@ -0,0 +1,34 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { getLinearWorkspaceLinkSummary } from './linear-workspace-link-summary' + +const mocks = vi.hoisted(() => ({ + translate: vi.fn((_key: string, fallback: string) => fallback) +})) + +vi.mock('@/i18n/i18n', () => ({ + translate: mocks.translate +})) + +describe('getLinearWorkspaceLinkSummary', () => { + beforeEach(() => { + mocks.translate.mockClear() + }) + + it('uses complete singular and plural workspace messages', () => { + getLinearWorkspaceLinkSummary(1) + getLinearWorkspaceLinkSummary(2) + + expect(mocks.translate).toHaveBeenNthCalledWith( + 1, + 'auto.components.onboarding.IntegrationsStep.oneWorkspaceLinked', + '{{count}} workspace linked. Add another workspace or replace a restricted key any time.', + { count: 1 } + ) + expect(mocks.translate).toHaveBeenNthCalledWith( + 2, + 'auto.components.onboarding.IntegrationsStep.manyWorkspacesLinked', + '{{count}} workspaces linked. Add another workspace or replace a restricted key any time.', + { count: 2 } + ) + }) +}) diff --git a/src/renderer/src/components/onboarding/linear-workspace-link-summary.ts b/src/renderer/src/components/onboarding/linear-workspace-link-summary.ts new file mode 100644 index 000000000000..e9150dae8b04 --- /dev/null +++ b/src/renderer/src/components/onboarding/linear-workspace-link-summary.ts @@ -0,0 +1,17 @@ +import { translate } from '@/i18n/i18n' + +// Why: the whole singular/plural sentence must remain translatable; an +// English suffix cannot express other grammars. +export function getLinearWorkspaceLinkSummary(count: number): string { + return count === 1 + ? translate( + 'auto.components.onboarding.IntegrationsStep.oneWorkspaceLinked', + '{{count}} workspace linked. Add another workspace or replace a restricted key any time.', + { count } + ) + : translate( + 'auto.components.onboarding.IntegrationsStep.manyWorkspacesLinked', + '{{count}} workspaces linked. Add another workspace or replace a restricted key any time.', + { count } + ) +} diff --git a/src/renderer/src/components/right-sidebar/SourceControl.tsx b/src/renderer/src/components/right-sidebar/SourceControl.tsx index 45a9e212c99a..348a970acece 100644 --- a/src/renderer/src/components/right-sidebar/SourceControl.tsx +++ b/src/renderer/src/components/right-sidebar/SourceControl.tsx @@ -491,6 +491,90 @@ function rewriteCompareBaseBranchFromCandidate( const EMPTY_GIT_STATUS_ENTRIES: GitStatusEntry[] = [] const EMPTY_BRANCH_CHANGE_ENTRIES: GitBranchChangeEntry[] = [] +function getFailedDiscardFileCountLabel(count: number): string { + // Why: file/files must remain inside the translated error sentence for + // locale-specific pluralization and word order. + return count === 1 + ? translate( + 'auto.components.right.sidebar.SourceControl.failedToDiscardOneFile', + 'Failed to discard {{count}} file', + { count } + ) + : translate( + 'auto.components.right.sidebar.SourceControl.failedToDiscardManyFiles', + 'Failed to discard {{count}} files', + { count } + ) +} + +function getDiffNoteCountLabel(count: number): string { + // Why: both source-control row variants need the same complete, + // locale-orderable note count phrase. + return count === 1 + ? translate('auto.components.right.sidebar.SourceControl.oneNote', '{{count}} note', { + count + }) + : translate('auto.components.right.sidebar.SourceControl.manyNotes', '{{count}} notes', { + count + }) +} + +function getAbortConflictOperationCopy(operation: AbortConflictOperation): { + title: string + description: string + confirmLabel: string + failedTitle: string + failedDescription: string +} { + // Why: merge/rebase are visible prose here, so each abort path owns full + // translated sentences instead of interpolating English operation names. + return operation === 'rebase' + ? { + title: translate( + 'auto.components.right.sidebar.SourceControl.abortRebaseTitle', + 'Abort rebase?' + ), + description: translate( + 'auto.components.right.sidebar.SourceControl.abortRebaseDescription', + 'This cancels the rebase in progress and can discard conflict resolutions made during this rebase.' + ), + confirmLabel: translate( + 'auto.components.right.sidebar.SourceControl.abortRebaseConfirmLabel', + 'Abort rebase' + ), + failedTitle: translate( + 'auto.components.right.sidebar.SourceControl.abortRebaseFailed', + 'Abort rebase failed' + ), + failedDescription: translate( + 'auto.components.right.sidebar.SourceControl.failedToAbortRebase', + 'Failed to abort rebase' + ) + } + : { + title: translate( + 'auto.components.right.sidebar.SourceControl.abortMergeTitle', + 'Abort merge?' + ), + description: translate( + 'auto.components.right.sidebar.SourceControl.abortMergeDescription', + 'This cancels the merge in progress and can discard conflict resolutions made during this merge.' + ), + confirmLabel: translate( + 'auto.components.right.sidebar.SourceControl.abortMergeConfirmLabel', + 'Abort merge' + ), + failedTitle: translate( + 'auto.components.right.sidebar.SourceControl.abortMergeFailed', + 'Abort merge failed' + ), + failedDescription: translate( + 'auto.components.right.sidebar.SourceControl.failedToAbortMerge', + 'Failed to abort merge' + ) + } +} + // Why: directional signifiers ahead of each primary action label. Commit // (✓) is affirmative; Push (↑) points in the direction data flows; Sync // (↕) is bidirectional; Publish gets a cloud-up to distinguish the @@ -2677,15 +2761,11 @@ function SourceControlInner(): React.JSX.Element { } const isRebase = requestedOperation === 'rebase' - const label = isRebase ? 'rebase' : 'merge' - const title = isRebase ? 'Abort rebase?' : 'Abort merge?' - const description = isRebase - ? 'This cancels the rebase in progress and can discard conflict resolutions made during this rebase.' - : 'This cancels the merge in progress and can discard conflict resolutions made during this merge.' + const actionCopy = getAbortConflictOperationCopy(requestedOperation) const confirmed = await confirmAction({ - title, - description, - confirmLabel: `Abort ${label}`, + title: actionCopy.title, + description: actionCopy.description, + confirmLabel: actionCopy.confirmLabel, confirmVariant: 'destructive' }) if (!confirmed) { @@ -2706,15 +2786,8 @@ function SourceControlInner(): React.JSX.Element { const abortGitOperation = isRebase ? abortRuntimeGitRebase : abortRuntimeGitMerge await abortGitOperation(context) } catch (error) { - const message = error instanceof Error ? error.message : `Failed to abort ${label}` - toast.error( - translate( - 'auto.components.right.sidebar.SourceControl.f99560ab29', - 'Abort {{value0}} failed', - { value0: label } - ), - { description: message } - ) + const message = error instanceof Error ? error.message : actionCopy.failedDescription + toast.error(actionCopy.failedTitle, { description: message }) setRemoteActionErrors((prev) => ({ ...prev, [activeWorktreeId]: { @@ -5452,22 +5525,15 @@ function SourceControlInner(): React.JSX.Element { const firstMsg = errors[0] instanceof Error ? errors[0].message : undefined const sample = result.failed.slice(0, 3).join(', ') const more = result.failed.length > 3 ? `, +${result.failed.length - 3} more` : '' - toast.error( - translate( - 'auto.components.right.sidebar.SourceControl.8eb3782a0c', - 'Failed to discard {{value0}} file{{value1}}', - { value0: result.failed.length, value1: result.failed.length === 1 ? '' : 's' } - ), - { - description: firstMsg - ? translate( - 'auto.components.right.sidebar.SourceControl.dc5a6465fc', - '{{value0}} (e.g. {{value1}}{{value2}})', - { value0: firstMsg, value1: sample, value2: more } - ) - : `${sample}${more}` - } - ) + toast.error(getFailedDiscardFileCountLabel(result.failed.length), { + description: firstMsg + ? translate( + 'auto.components.right.sidebar.SourceControl.dc5a6465fc', + '{{value0}} (e.g. {{value1}}{{value2}})', + { value0: firstMsg, value1: sample, value2: more } + ) + : `${sample}${more}` + }) } if (!result.aborted) { await refreshActiveGitStatusAfterMutation() @@ -8068,11 +8134,7 @@ const UncommittedEntryRow = React.memo(function UncommittedEntryRow({ // attached, without opening the Notes tab. {commentCount} @@ -8257,11 +8319,7 @@ function BranchEntryRow({ {commentCount > 0 && ( {commentCount} diff --git a/src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.test.ts b/src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.test.ts index f21b136e4916..14f91c8555f4 100644 --- a/src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.test.ts +++ b/src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.test.ts @@ -66,6 +66,12 @@ describe('diff comments clear dialog state', () => { expect(formatPendingDiffCommentsClearDescription(filePending, 2)).toBe( 'Clear 2 notes from src/app.tsx?' ) + expect(formatPendingDiffCommentsClearDescription(allPending, 2)).toBe( + 'Clear 2 notes from this workspace?' + ) + expect(formatPendingDiffCommentsClearDescription(filePending, 1)).toBe( + 'Clear 1 note from src/app.tsx?' + ) expect(formatPendingDiffCommentsClearDescription(null, 0)).toBe('') }) }) diff --git a/src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.ts b/src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.ts index 584ca1cd7e72..4e59af3a0844 100644 --- a/src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.ts +++ b/src/renderer/src/components/right-sidebar/diff-comments-clear-dialog-state.ts @@ -1,3 +1,5 @@ +import { translate } from '@/i18n/i18n' + export type PendingDiffCommentsClear = | { kind: 'all'; worktreeId: string } | { kind: 'file'; worktreeId: string; filePath: string } @@ -43,9 +45,30 @@ export function formatPendingDiffCommentsClearDescription( if (!pending) { return '' } - const noun = count === 1 ? 'note' : 'notes' + // Why: scope and plurality both affect sentence grammar, so each combination + // is translated as a complete confirmation question. if (pending.kind === 'all') { - return `Clear ${count} ${noun} from this workspace?` + return count === 1 + ? translate( + 'auto.components.right.sidebar.diff.comments.clear.dialog.state.f07232e970', + 'Clear {{count}} note from this workspace?', + { count } + ) + : translate( + 'auto.components.right.sidebar.diff.comments.clear.dialog.state.d10b0441e0', + 'Clear {{count}} notes from this workspace?', + { count } + ) } - return `Clear ${count} ${noun} from ${pending.filePath}?` + return count === 1 + ? translate( + 'auto.components.right.sidebar.diff.comments.clear.dialog.state.bd98da62a0', + 'Clear {{count}} note from {{value0}}?', + { count, value0: pending.filePath } + ) + : translate( + 'auto.components.right.sidebar.diff.comments.clear.dialog.state.edd639023e', + 'Clear {{count}} notes from {{value0}}?', + { count, value0: pending.filePath } + ) } diff --git a/src/renderer/src/components/right-sidebar/file-deletion-localized-copy.ts b/src/renderer/src/components/right-sidebar/file-deletion-localized-copy.ts new file mode 100644 index 000000000000..301ac5c71e21 --- /dev/null +++ b/src/renderer/src/components/right-sidebar/file-deletion-localized-copy.ts @@ -0,0 +1,41 @@ +import { translate } from '@/i18n/i18n' + +export function formatFileDeletionFailure(args: { + name: string + isRemote: boolean + isWindows: boolean +}): string { + // Why: the action and OS destination change sentence structure across + // languages, so they cannot be injected as English verb fragments. + if (args.isRemote) { + return translate( + 'auto.components.right.sidebar.file.deletion.localized.copy.222a8a3fb3', + "Failed to delete '{{value0}}'.", + { value0: args.name } + ) + } + return args.isWindows + ? translate( + 'auto.components.right.sidebar.file.deletion.localized.copy.e9605383fd', + "Failed to move '{{value0}}' to the Recycle Bin.", + { value0: args.name } + ) + : translate( + 'auto.components.right.sidebar.file.deletion.localized.copy.f5fbc0837d', + "Failed to move '{{value0}}' to the Trash.", + { value0: args.name } + ) +} + +export function formatMixedFileDeletionDescription(isWindows: boolean): string { + // Why: Recycle Bin and Trash are localized product terms, not data values. + return isWindows + ? translate( + 'auto.components.right.sidebar.file.deletion.localized.copy.f583bf91df', + 'Remote items are permanently deleted and cannot be undone. Local items move to the Recycle Bin.' + ) + : translate( + 'auto.components.right.sidebar.file.deletion.localized.copy.b1bde1b2e4', + 'Remote items are permanently deleted and cannot be undone. Local items move to the Trash.' + ) +} diff --git a/src/renderer/src/components/right-sidebar/source-control-dropdown-items.ts b/src/renderer/src/components/right-sidebar/source-control-dropdown-items.ts index 62801dcfe312..e0c18a2a3bea 100644 --- a/src/renderer/src/components/right-sidebar/source-control-dropdown-items.ts +++ b/src/renderer/src/components/right-sidebar/source-control-dropdown-items.ts @@ -577,13 +577,37 @@ export function resolveDropdownItems(inputs: DropdownActionInputs): DropdownEntr ] if (conflictOperation === 'merge' || conflictOperation === 'rebase') { const isRebase = conflictOperation === 'rebase' - const label = isRebase ? 'Abort rebase' : 'Abort merge' + // Why: operation names change grammar in translated commands, so merge and + // rebase each use complete label and title translation units. + const label = isRebase + ? translate( + 'auto.components.right.sidebar.source.control.dropdown.items.ac19d0917c', + 'Abort rebase' + ) + : translate( + 'auto.components.right.sidebar.source.control.dropdown.items.515a94f9f6', + 'Abort merge' + ) + const title = globalBusy + ? translate( + 'auto.components.right.sidebar.source.control.dropdown.items.f48d7f6445', + 'Operation in progress…' + ) + : isRebase + ? translate( + 'auto.components.right.sidebar.source.control.dropdown.items.62e79d363d', + 'Abort the rebase in progress' + ) + : translate( + 'auto.components.right.sidebar.source.control.dropdown.items.4ef3aa6987', + 'Abort the merge in progress' + ) entries.push( { kind: 'separator' }, { kind: isRebase ? 'abort_rebase' : 'abort_merge', label, - title: globalBusy ? 'Operation in progress…' : `Abort the ${conflictOperation} in progress`, + title, disabled: globalBusy, variant: 'destructive' } diff --git a/src/renderer/src/components/right-sidebar/use-hosted-review-actions.test.tsx b/src/renderer/src/components/right-sidebar/use-hosted-review-actions.test.tsx index 82527dc8ed50..3e9997f68e39 100644 --- a/src/renderer/src/components/right-sidebar/use-hosted-review-actions.test.tsx +++ b/src/renderer/src/components/right-sidebar/use-hosted-review-actions.test.tsx @@ -4,7 +4,12 @@ import { act, createElement } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { PRInfo, Repo } from '../../../../shared/types' -import { useHostedReviewActions, type HostedReviewActionInfo } from './use-hosted-review-actions' +import { + buildHostedReviewStateConfirmation, + formatHostedReviewStateChangeFailure, + useHostedReviewActions, + type HostedReviewActionInfo +} from './use-hosted-review-actions' const confirmationMocks = vi.hoisted(() => ({ confirm: vi.fn() @@ -140,4 +145,42 @@ describe('useHostedReviewActions', () => { expect(runtimeRpcMocks.callRuntimeRpc).not.toHaveBeenCalled() expect(onRefreshReview).toHaveBeenCalledTimes(1) }) + + it('builds complete localized close and reopen confirmations', () => { + expect( + buildHostedReviewStateConfirmation({ + nextState: 'closed', + shortLabel: 'PR', + isGitLab: false, + reviewNumber: 1015 + }) + ).toEqual({ + title: 'Close PR #1015?', + description: 'This will close the pull request.', + confirmLabel: 'Close', + confirmVariant: 'destructive' + }) + expect( + buildHostedReviewStateConfirmation({ + nextState: 'open', + shortLabel: 'MR', + isGitLab: true, + reviewNumber: 42 + }) + ).toEqual({ + title: 'Reopen MR !42?', + description: 'This will reopen the merge request.', + confirmLabel: 'Reopen', + confirmVariant: 'default' + }) + }) + + it('formats complete fallback failures for both state transitions', () => { + expect(formatHostedReviewStateChangeFailure('closed', false)).toBe( + 'Failed to close the pull request.' + ) + expect(formatHostedReviewStateChangeFailure('open', true)).toBe( + 'Failed to reopen the merge request.' + ) + }) }) diff --git a/src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts b/src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts index ed3a1b23bcca..96519d6d55b8 100644 --- a/src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts +++ b/src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts @@ -27,6 +27,86 @@ export type HostedReviewActionInfo = Pick< > > +export function buildHostedReviewStateConfirmation(args: { + nextState: 'open' | 'closed' + shortLabel: string + isGitLab: boolean + reviewNumber: number +}): { + title: string + description: string + confirmLabel: string + confirmVariant: 'default' | 'destructive' +} { + const isClosing = args.nextState === 'closed' + const reviewReference = `${args.shortLabel} ${args.isGitLab ? '!' : '#'}${args.reviewNumber}` + const localizedReviewLabel = args.isGitLab + ? translate( + 'auto.components.right.sidebar.HostedReviewActions.mergeRequestLabel', + 'merge request' + ) + : translate( + 'auto.components.right.sidebar.HostedReviewActions.pullRequestLabel', + 'pull request' + ) + // Why: Close/Reopen are sentence grammar, while the localized PR/MR + // reference is data that translators can position within the full title. + return { + title: isClosing + ? translate( + 'auto.components.right.sidebar.HostedReviewActions.57fb10cfe7', + 'Close {{value0}}?', + { value0: reviewReference } + ) + : translate( + 'auto.components.right.sidebar.HostedReviewActions.b982b128c0', + 'Reopen {{value0}}?', + { value0: reviewReference } + ), + description: isClosing + ? translate( + 'auto.components.right.sidebar.HostedReviewActions.a3d572a4de', + 'This will close the {{value0}}.', + { value0: localizedReviewLabel } + ) + : translate( + 'auto.components.right.sidebar.HostedReviewActions.78f5ff294c', + 'This will reopen the {{value0}}.', + { value0: localizedReviewLabel } + ), + confirmLabel: isClosing + ? translate('auto.components.right.sidebar.HostedReviewActions.73e5cc9c99', 'Close') + : translate('auto.components.right.sidebar.HostedReviewActions.641c8ec02a', 'Reopen'), + confirmVariant: isClosing ? 'destructive' : 'default' + } +} + +export function formatHostedReviewStateChangeFailure( + nextState: 'open' | 'closed', + isGitLab: boolean +): string { + const localizedReviewLabel = isGitLab + ? translate( + 'auto.components.right.sidebar.HostedReviewActions.mergeRequestLabel', + 'merge request' + ) + : translate( + 'auto.components.right.sidebar.HostedReviewActions.pullRequestLabel', + 'pull request' + ) + return nextState === 'closed' + ? translate( + 'auto.components.right.sidebar.HostedReviewActions.72c9a274a7', + 'Failed to close the {{value0}}.', + { value0: localizedReviewLabel } + ) + : translate( + 'auto.components.right.sidebar.HostedReviewActions.93afca5395', + 'Failed to reopen the {{value0}}.', + { value0: localizedReviewLabel } + ) +} + export function useHostedReviewActions({ review, githubPR, @@ -134,23 +214,14 @@ export function useHostedReviewActions({ return } const isClosing = nextState === 'closed' - const label = isClosing ? 'Close' : 'Reopen' - const confirmed = await confirm({ - title: `${label} ${shortLabel} ${isGitLab ? '!' : '#'}${review.number}?`, - description: isClosing - ? translate( - 'auto.components.right.sidebar.HostedReviewActions.a3d572a4de', - 'This will close the {{value0}}.', - { value0: reviewLabel } - ) - : translate( - 'auto.components.right.sidebar.HostedReviewActions.78f5ff294c', - 'This will reopen the {{value0}}.', - { value0: reviewLabel } - ), - confirmLabel: label, - confirmVariant: isClosing ? 'destructive' : 'default' - }) + const confirmed = await confirm( + buildHostedReviewStateConfirmation({ + nextState, + shortLabel, + isGitLab, + reviewNumber: review.number + }) + ) if (!confirmed) { return } @@ -181,7 +252,7 @@ export function useHostedReviewActions({ toast.success( isClosing ? translate( - 'auto.components.right.sidebar.HostedReviewActions.fa3ee9a515', + 'auto.components.right.sidebar.HostedReviewActions.reviewClosedToast', '{{value0}} closed', { value0: shortLabel } ) @@ -195,7 +266,9 @@ export function useHostedReviewActions({ } } catch (err) { const message = - err instanceof Error ? err.message : `Failed to ${label.toLowerCase()} ${reviewLabel}` + err instanceof Error + ? err.message + : formatHostedReviewStateChangeFailure(nextState, isGitLab) setActionError(message) toast.error(message) } finally { diff --git a/src/renderer/src/components/right-sidebar/useFileDeletion.test.ts b/src/renderer/src/components/right-sidebar/useFileDeletion.test.ts new file mode 100644 index 000000000000..b9d5c3f0c9d4 --- /dev/null +++ b/src/renderer/src/components/right-sidebar/useFileDeletion.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from 'vitest' +import { + formatFileDeletionFailure, + formatMixedFileDeletionDescription +} from './file-deletion-localized-copy' + +describe('localized file deletion copy', () => { + it('formats complete remote, Windows, and macOS/Linux failure sentences', () => { + expect(formatFileDeletionFailure({ name: 'a.ts', isRemote: true, isWindows: false })).toBe( + "Failed to delete 'a.ts'." + ) + expect(formatFileDeletionFailure({ name: 'a.ts', isRemote: false, isWindows: true })).toBe( + "Failed to move 'a.ts' to the Recycle Bin." + ) + expect(formatFileDeletionFailure({ name: 'a.ts', isRemote: false, isWindows: false })).toBe( + "Failed to move 'a.ts' to the Trash." + ) + }) + + it('keeps platform trash names inside their full batch descriptions', () => { + expect(formatMixedFileDeletionDescription(true)).toBe( + 'Remote items are permanently deleted and cannot be undone. Local items move to the Recycle Bin.' + ) + expect(formatMixedFileDeletionDescription(false)).toBe( + 'Remote items are permanently deleted and cannot be undone. Local items move to the Trash.' + ) + }) +}) diff --git a/src/renderer/src/components/right-sidebar/useFileDeletion.ts b/src/renderer/src/components/right-sidebar/useFileDeletion.ts index ce15a9d493bb..115992b6299f 100644 --- a/src/renderer/src/components/right-sidebar/useFileDeletion.ts +++ b/src/renderer/src/components/right-sidebar/useFileDeletion.ts @@ -19,6 +19,10 @@ import { writeRuntimeFile } from '@/runtime/runtime-file-client' import { translate } from '@/i18n/i18n' +import { + formatFileDeletionFailure, + formatMixedFileDeletionDescription +} from './file-deletion-localized-copy' type UseFileDeletionParams = { activeWorktreeId: string | null @@ -217,15 +221,10 @@ export function useFileDeletion({ return true } catch (error) { - const action = isRemote ? 'delete' : isWindows ? 'move to Recycle Bin' : 'move to Trash' toast.error( error instanceof Error ? error.message - : translate( - 'auto.components.right.sidebar.useFileDeletion.72691dfebc', - "Failed to {{value0}} '{{value1}}'.", - { value0: action, value1: node.name } - ) + : formatFileDeletionFailure({ name: node.name, isRemote, isWindows }) ) return false } finally { @@ -269,7 +268,6 @@ export function useFileDeletion({ // but the selection can also include local roots that only go to the // Trash — so a mixed batch needs copy that keeps that distinction. const hasLocalDelete = roots.some(isLocalDeleteNode) - const trashName = isWindows ? 'Recycle Bin' : 'Trash' // Why: selection is cleared once after the entire batch settles rather // than per-node, so no concurrent completion can restore a partial // stale set. @@ -296,11 +294,7 @@ export function useFileDeletion({ { count: nodes.length } ), description: hasLocalDelete - ? translate( - 'auto.components.right.sidebar.useFileDeletion.fca915a67a', - 'Remote items are permanently deleted and cannot be undone. Local items move to the {{value0}}.', - { value0: trashName } - ) + ? formatMixedFileDeletionDescription(isWindows) : translate( 'auto.components.right.sidebar.useFileDeletion.dd029aa5cd', 'This permanently deletes the selected items and any directory contents on the remote host. This cannot be undone.' diff --git a/src/renderer/src/components/right-sidebar/useFileExplorerImport.test.ts b/src/renderer/src/components/right-sidebar/useFileExplorerImport.test.ts new file mode 100644 index 000000000000..a88a2fa89380 --- /dev/null +++ b/src/renderer/src/components/right-sidebar/useFileExplorerImport.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest' +import { + formatFileExplorerImportFailure, + formatFileExplorerImportSkipped +} from './useFileExplorerImport' + +describe('localized file explorer import copy', () => { + it('uses complete singular and plural failure sentences', () => { + expect(formatFileExplorerImportFailure(1)).toBe('Failed to import 1 file.') + expect(formatFileExplorerImportFailure(3)).toBe('Failed to import 3 files.') + }) + + it('uses complete singular and plural skipped sentences', () => { + expect(formatFileExplorerImportSkipped(1)).toBe('Skipped 1 file.') + expect(formatFileExplorerImportSkipped(3)).toBe('Skipped 3 files.') + }) +}) diff --git a/src/renderer/src/components/right-sidebar/useFileExplorerImport.ts b/src/renderer/src/components/right-sidebar/useFileExplorerImport.ts index 8e0951cf9bf1..50f08001d5ab 100644 --- a/src/renderer/src/components/right-sidebar/useFileExplorerImport.ts +++ b/src/renderer/src/components/right-sidebar/useFileExplorerImport.ts @@ -14,6 +14,36 @@ type UseFileExplorerImportParams = { setSelectedPath: (path: string | null) => void } +export function formatFileExplorerImportFailure(count: number): string { + // Why: noun inflection belongs to the translated sentence, not an English + // "file/files" fragment passed through interpolation. + return count === 1 + ? translate( + 'auto.components.right.sidebar.useFileExplorerImport.4222b43cc1', + 'Failed to import {{count}} file.', + { count } + ) + : translate( + 'auto.components.right.sidebar.useFileExplorerImport.576340ce05', + 'Failed to import {{count}} files.', + { count } + ) +} + +export function formatFileExplorerImportSkipped(count: number): string { + return count === 1 + ? translate( + 'auto.components.right.sidebar.useFileExplorerImport.d68e492480', + 'Skipped {{count}} file.', + { count } + ) + : translate( + 'auto.components.right.sidebar.useFileExplorerImport.17be0f9168', + 'Skipped {{count}} files.', + { count } + ) +} + /** * Subscribes to native file-drop events targeted at the file explorer and * runs the import pipeline: copy into worktree, refresh, reveal. @@ -89,26 +119,20 @@ export function useFileExplorerImport({ } if (failed.length > 0) { - const noun = failed.length === 1 ? 'file' : 'files' - toast.error( - translate( - 'auto.components.right.sidebar.useFileExplorerImport.132fd0e1e9', - 'Failed to import {{value0}} {{value1}}.', - { value0: failed.length, value1: noun } - ) - ) + toast.error(formatFileExplorerImportFailure(failed.length)) } else if (skipped.length > 0 && imported.length === 0) { - const noun = skipped.length === 1 ? 'file' : 'files' - toast.error( + toast.error(formatFileExplorerImportSkipped(skipped.length)) + } + } catch (err) { + toast.error( + extractIpcErrorMessage( + err, translate( - 'auto.components.right.sidebar.useFileExplorerImport.25919b2050', - 'Skipped {{value0}} {{value1}}.', - { value0: skipped.length, value1: noun } + 'auto.components.right.sidebar.useFileExplorerImport.58220fc4bb', + 'Failed to import files.' ) ) - } - } catch (err) { - toast.error(extractIpcErrorMessage(err, 'Failed to import files.')) + ) } finally { clearNativeDragStateRef.current() } diff --git a/src/renderer/src/components/settings/AppearancePane.test.tsx b/src/renderer/src/components/settings/AppearancePane.test.tsx index ca68ff7e47c0..13bcf83e98f0 100644 --- a/src/renderer/src/components/settings/AppearancePane.test.tsx +++ b/src/renderer/src/components/settings/AppearancePane.test.tsx @@ -197,7 +197,7 @@ describe('AppearancePane', () => { delete (window as unknown as { api?: unknown }).api }) - it('renders the language dropdown with system, english, chinese, korean, japanese, and spanish options', async () => { + it('renders Simplified and Traditional Chinese as separate language options', async () => { mocks.state.settingsSearchQuery = 'language' const updateSettings = vi.fn() const settings = { @@ -212,12 +212,17 @@ describe('AppearancePane', () => { const chineseOption = container.querySelector( '[data-slot="select-item"][data-value="zh"]' ) + const traditionalChineseOption = container.querySelector( + '[data-slot="select-item"][data-value="zh-TW"]' + ) expect(languageTrigger).not.toBeNull() expect(chineseOption).not.toBeNull() + expect(traditionalChineseOption).not.toBeNull() expect(container.textContent).toContain('System') expect(container.textContent).toContain('English') expect(container.textContent).toContain('中文(简体)') + expect(container.textContent).toContain('中文(繁體)') expect(container.textContent).toContain('한국어') expect(container.textContent).toContain('日本語') expect(container.textContent).toContain('Español') diff --git a/src/renderer/src/components/settings/RepositoryIconTabs.tsx b/src/renderer/src/components/settings/RepositoryIconTabs.tsx index d0507a89db4d..7f947f6e1577 100644 --- a/src/renderer/src/components/settings/RepositoryIconTabs.tsx +++ b/src/renderer/src/components/settings/RepositoryIconTabs.tsx @@ -164,7 +164,7 @@ export function RepositoryIconTabs({ className="size-8" onClick={() => onSetIcon({ type: 'lucide', name: option.name })} aria-label={translate( - 'auto.components.settings.RepositoryIconPicker.2b7d27b93c', + 'auto.components.settings.RepositoryIconPicker.useRepoIcon', 'Use {{value0}} repo icon', { value0: option.label } )} @@ -190,7 +190,7 @@ export function RepositoryIconTabs({ className="size-8 text-base" onClick={() => onSetIcon({ type: 'emoji', emoji })} aria-label={translate( - 'auto.components.settings.RepositoryIconPicker.2b7d27b93c', + 'auto.components.settings.RepositoryIconPicker.useRepoIcon', 'Use {{value0}} repo icon', { value0: emoji } )} diff --git a/src/renderer/src/components/settings/SshPane.tsx b/src/renderer/src/components/settings/SshPane.tsx index 2e66ca3823a7..03e983b065db 100644 --- a/src/renderer/src/components/settings/SshPane.tsx +++ b/src/renderer/src/components/settings/SshPane.tsx @@ -9,6 +9,7 @@ import { Button } from '../ui/button' import { removeSshTargetWithBestEffortCleanup } from './ssh-target-remove' import { SshTargetCard } from './SshTargetCard' import { SshTargetDestructiveActions } from './SshTargetDestructiveActions' +import { getSyncedSshServerSummary } from './ssh-import-summary' import { SshTargetForm, EMPTY_FORM, type EditingTarget } from './SshTargetForm' import { getEditingTargetForSshTarget } from './ssh-target-draft' import { buildSshTargetSavePayload } from './ssh-target-save-payload' @@ -297,13 +298,7 @@ export function SshPane(): React.JSX.Element { if (result.targets.length === 0) { toast('~/.ssh/config already in sync') } else { - toast.success( - translate( - 'auto.components.settings.SshPane.f8050f6307', - 'Synced {{value0}} server{{value1}}', - { value0: result.targets.length, value1: result.targets.length > 1 ? 's' : '' } - ) - ) + toast.success(getSyncedSshServerSummary(result.targets.length)) } } await loadTargets() diff --git a/src/renderer/src/components/settings/jira-integration-card.test.tsx b/src/renderer/src/components/settings/jira-integration-card.test.tsx index 50b3990748c0..62e468164bd4 100644 --- a/src/renderer/src/components/settings/jira-integration-card.test.tsx +++ b/src/renderer/src/components/settings/jira-integration-card.test.tsx @@ -45,18 +45,16 @@ vi.mock('@/components/jira-connect-dialog', () => ({ let root: Root | null = null let container: HTMLDivElement | null = null -function installStore(settings: StoreState['settings']): StoreState { +function installStore(settings: StoreState['settings'], siteCount = 1): StoreState { const state: StoreState = { jiraStatus: { connected: true, - sites: [ - { - id: 'site-1', - displayName: 'Acme Jira', - siteUrl: 'https://acme.atlassian.net', - email: 'jira@example.test' - } - ] + sites: Array.from({ length: siteCount }, (_, index) => ({ + id: `site-${index + 1}`, + displayName: index === 0 ? 'Acme Jira' : `Acme Jira ${index + 1}`, + siteUrl: `https://acme${index || ''}.atlassian.net`, + email: 'jira@example.test' + })) }, jiraStatusChecked: true, jiraStatusContextKey: getProviderRuntimeContextKey(settings), @@ -100,6 +98,7 @@ describe('JiraIntegrationCard account scope', () => { const rendered = await renderCard() expect(rendered.textContent).toContain('Account scope: Remote server: runtime-1') + expect(rendered.textContent).toContain('1 site connected') expect(rendered.textContent).toContain('Acme Jira') expect(rendered.textContent).toContain('https://acme.atlassian.net · jira@example.test') @@ -116,4 +115,12 @@ describe('JiraIntegrationCard account scope', () => { sectionId: 'default-runtime' }) }) + + it('uses the complete plural connected-site message', async () => { + installStore({ activeRuntimeEnvironmentId: null }, 2) + + const rendered = await renderCard() + + expect(rendered.textContent).toContain('2 sites connected') + }) }) diff --git a/src/renderer/src/components/settings/jira-integration-card.tsx b/src/renderer/src/components/settings/jira-integration-card.tsx index 0501d05faf16..59654ae3484b 100644 --- a/src/renderer/src/components/settings/jira-integration-card.tsx +++ b/src/renderer/src/components/settings/jira-integration-card.tsx @@ -74,11 +74,19 @@ export function JiraIntegrationCard(): React.JSX.Element { name="Jira" description={ connected - ? translate( - 'auto.components.settings.task.tracker.integration.cards.9fa04a032e', - '{{value0}} site{{value1}} connected', - { value0: siteCount, value1: siteCount === 1 ? '' : 's' } - ) + ? // Why: site count is numeric data, while each complete noun form + // stays translatable for languages that do not use English suffixes. + siteCount === 1 + ? translate( + 'auto.components.settings.task.tracker.integration.cards.oneJiraSiteConnected', + '{{count}} site connected', + { count: siteCount } + ) + : translate( + 'auto.components.settings.task.tracker.integration.cards.manyJiraSitesConnected', + '{{count}} sites connected', + { count: siteCount } + ) : checking ? translate( 'auto.components.settings.task.tracker.integration.cards.a1093a06c7', diff --git a/src/renderer/src/components/settings/ssh-import-summary.test.ts b/src/renderer/src/components/settings/ssh-import-summary.test.ts new file mode 100644 index 000000000000..c95be2c05a0f --- /dev/null +++ b/src/renderer/src/components/settings/ssh-import-summary.test.ts @@ -0,0 +1,52 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { getSyncedSshHostSummary, getSyncedSshServerSummary } from './ssh-import-summary' + +const mocks = vi.hoisted(() => ({ + translate: vi.fn((_key: string, fallback: string) => fallback) +})) + +vi.mock('@/i18n/i18n', () => ({ + translate: mocks.translate +})) + +describe('SSH import summary copy', () => { + beforeEach(() => { + mocks.translate.mockClear() + }) + + it('uses complete singular and plural server messages', () => { + getSyncedSshServerSummary(1) + getSyncedSshServerSummary(3) + + expect(mocks.translate).toHaveBeenNthCalledWith( + 1, + 'auto.components.settings.SshPane.syncedOneServer', + 'Synced {{count}} server', + { count: 1 } + ) + expect(mocks.translate).toHaveBeenNthCalledWith( + 2, + 'auto.components.settings.SshPane.syncedManyServers', + 'Synced {{count}} servers', + { count: 3 } + ) + }) + + it('uses complete singular and plural host messages', () => { + getSyncedSshHostSummary(1) + getSyncedSshHostSummary(3) + + expect(mocks.translate).toHaveBeenNthCalledWith( + 1, + 'auto.components.sidebar.AddRemoteHostDialog.sshImportSyncedOneHost', + 'Synced {{count}} host.', + { count: 1 } + ) + expect(mocks.translate).toHaveBeenNthCalledWith( + 2, + 'auto.components.sidebar.AddRemoteHostDialog.sshImportSyncedManyHosts', + 'Synced {{count}} hosts.', + { count: 3 } + ) + }) +}) diff --git a/src/renderer/src/components/settings/ssh-import-summary.ts b/src/renderer/src/components/settings/ssh-import-summary.ts new file mode 100644 index 000000000000..e4a0fad68b9a --- /dev/null +++ b/src/renderer/src/components/settings/ssh-import-summary.ts @@ -0,0 +1,27 @@ +import { translate } from '@/i18n/i18n' + +// Why: translators need complete count variants; English suffixes cannot be +// reordered or declined in other languages. +export function getSyncedSshServerSummary(count: number): string { + return count === 1 + ? translate('auto.components.settings.SshPane.syncedOneServer', 'Synced {{count}} server', { + count + }) + : translate('auto.components.settings.SshPane.syncedManyServers', 'Synced {{count}} servers', { + count + }) +} + +export function getSyncedSshHostSummary(count: number): string { + return count === 1 + ? translate( + 'auto.components.sidebar.AddRemoteHostDialog.sshImportSyncedOneHost', + 'Synced {{count}} host.', + { count } + ) + : translate( + 'auto.components.sidebar.AddRemoteHostDialog.sshImportSyncedManyHosts', + 'Synced {{count}} hosts.', + { count } + ) +} diff --git a/src/renderer/src/components/settings/task-tracker-integration-cards.test.tsx b/src/renderer/src/components/settings/task-tracker-integration-cards.test.tsx index 9fce3fc84fc0..360935357b53 100644 --- a/src/renderer/src/components/settings/task-tracker-integration-cards.test.tsx +++ b/src/renderer/src/components/settings/task-tracker-integration-cards.test.tsx @@ -52,20 +52,19 @@ let container: HTMLDivElement | null = null function installStore( connected: boolean, - settings: StoreState['settings'] = { activeRuntimeEnvironmentId: null } + settings: StoreState['settings'] = { activeRuntimeEnvironmentId: null }, + workspaceCount = 1 ): StoreState { const state: StoreState = { linearStatus: { connected, workspaces: connected - ? [ - { - id: 'workspace-1', - organizationName: 'Acme', - displayName: 'Acme workspace', - email: 'linear@example.test' - } - ] + ? Array.from({ length: workspaceCount }, (_, index) => ({ + id: `workspace-${index + 1}`, + organizationName: index === 0 ? 'Acme' : `Acme ${index + 1}`, + displayName: index === 0 ? 'Acme workspace' : `Acme workspace ${index + 1}`, + email: 'linear@example.test' + })) : [] }, linearStatusChecked: true, @@ -148,6 +147,7 @@ describe('LinearIntegrationCard account scope', () => { const rendered = await renderCard() expect(rendered.textContent).toContain('Account scope: Remote server: runtime-1') + expect(rendered.textContent).toContain('1 workspace connected') expect(rendered.textContent).toContain( 'Credentials and account checks for this provider are owned by this remote server. Use Settings > Remote Orca Servers > Advanced to edit another default runtime scope.' ) @@ -174,6 +174,14 @@ describe('LinearIntegrationCard account scope', () => { expect(state.testLinearConnection).toHaveBeenCalledWith('workspace-1') }) + it('uses the complete plural connected-workspace message', async () => { + installStore(true, { activeRuntimeEnvironmentId: null }, 2) + + const rendered = await renderCard() + + expect(rendered.textContent).toContain('2 workspaces connected') + }) + it('clears verification state after adding another Linear workspace', async () => { installStore(true) const rendered = await renderCard() diff --git a/src/renderer/src/components/settings/task-tracker-integration-cards.tsx b/src/renderer/src/components/settings/task-tracker-integration-cards.tsx index 6c2cc1b95755..26fe60ebc45a 100644 --- a/src/renderer/src/components/settings/task-tracker-integration-cards.tsx +++ b/src/renderer/src/components/settings/task-tracker-integration-cards.tsx @@ -72,11 +72,19 @@ export function LinearIntegrationCard(): React.JSX.Element { name="Linear" description={ connected - ? translate( - 'auto.components.settings.task.tracker.integration.cards.e1f5e6424c', - '{{value0}} workspace{{value1}} connected', - { value0: workspaces.length, value1: workspaces.length === 1 ? '' : 's' } - ) + ? // Why: translators need complete count variants instead of an + // interpolated English suffix that cannot be reordered. + workspaces.length === 1 + ? translate( + 'auto.components.settings.task.tracker.integration.cards.oneLinearWorkspaceConnected', + '{{count}} workspace connected', + { count: workspaces.length } + ) + : translate( + 'auto.components.settings.task.tracker.integration.cards.manyLinearWorkspacesConnected', + '{{count}} workspaces connected', + { count: workspaces.length } + ) : checking ? translate( 'auto.components.settings.task.tracker.integration.cards.fe9231215b', diff --git a/src/renderer/src/components/sidebar/AddRemoteHostDialog.tsx b/src/renderer/src/components/sidebar/AddRemoteHostDialog.tsx index 705d5537073e..9f5b74e5d35f 100644 --- a/src/renderer/src/components/sidebar/AddRemoteHostDialog.tsx +++ b/src/renderer/src/components/sidebar/AddRemoteHostDialog.tsx @@ -18,6 +18,7 @@ import { parseRelayGracePeriodSeconds, type EditingTarget } from '../settings/ssh-target-draft' +import { getSyncedSshHostSummary } from '../settings/ssh-import-summary' import { MAX_SSH_RELAY_GRACE_PERIOD_SECONDS, type SshTarget } from '../../../../shared/ssh-types' import { RemoteServerFields, SshHostFields } from './AddRemoteHostFields' @@ -146,13 +147,7 @@ export function AddRemoteHostDialog({ ) ) } else { - toast.success( - translate( - 'auto.components.sidebar.AddRemoteHostDialog.sshImportSynced', - 'Synced {{value0}} host{{value1}}.', - { value0: synced.length, value1: synced.length > 1 ? 's' : '' } - ) - ) + toast.success(getSyncedSshHostSummary(synced.length)) reset() onOpenChange(null) } diff --git a/src/renderer/src/components/sidebar/ImportedWorktreesVisibilityLine.tsx b/src/renderer/src/components/sidebar/ImportedWorktreesVisibilityLine.tsx index 3c82858579e0..7311ca4bb9ce 100644 --- a/src/renderer/src/components/sidebar/ImportedWorktreesVisibilityLine.tsx +++ b/src/renderer/src/components/sidebar/ImportedWorktreesVisibilityLine.tsx @@ -29,7 +29,6 @@ type ImportedWorktreesVisibilityLineProps = { } const PREVIEW_LIMIT = 3 -const KEEP_HIDDEN_LABEL = 'Keep hidden - recover from the project menu' const GROUP_LIMIT = 5 type ImportedWorktreePathGroup = { @@ -37,10 +36,6 @@ type ImportedWorktreePathGroup = { worktrees: ImportedWorktreeVisibilityPreview[] } -function pluralizeWorktree(count: number): string { - return count === 1 ? 'worktree' : 'worktrees' -} - function getWorktreeKey( worktree: ImportedWorktreeVisibilityPreview, index: number, @@ -85,20 +80,66 @@ export default function ImportedWorktreesVisibilityLine({ const [isExpanded, setIsExpanded] = useState(false) const [expandedGroupPathKeys, setExpandedGroupPathKeys] = useState>(new Set()) const hiddenCount = hiddenWorktrees.length - const worktreeNoun = pluralizeWorktree(hiddenCount) const worktreeGroups = groupWorktreesByParentPath(hiddenWorktrees) const visibleWorktreeGroups = worktreeGroups.slice(0, GROUP_LIMIT) const remainingGroupCount = Math.max(0, worktreeGroups.length - visibleWorktreeGroups.length) - const keepHiddenAriaLabel = `Keep ${hiddenCount} discovered ${worktreeNoun} hidden for ${repoDisplayName}; recover from the project menu` + const keepHiddenAriaLabel = + hiddenCount === 1 + ? translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.keepOneHiddenForRepo', + 'Keep 1 discovered worktree hidden for {{value0}}; recover from the project menu', + { value0: repoDisplayName } + ) + : translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.keepManyHiddenForRepo', + 'Keep {{count}} discovered worktrees hidden for {{value0}}; recover from the project menu', + { count: hiddenCount, value0: repoDisplayName } + ) + const keepHiddenLabel = translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.keepHiddenRecover', + 'Keep hidden - recover from the project menu' + ) + // Why: actions must be translated as complete sentences; interpolating English verbs leaks them into CJK labels. + const hiddenWorktreeDisclosureLabel = isExpanded + ? translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.collapseHiddenWorktrees', + 'Collapse hidden worktrees for {{value0}}', + { value0: repoDisplayName } + ) + : translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.expandHiddenWorktrees', + 'Expand hidden worktrees for {{value0}}', + { value0: repoDisplayName } + ) if (hiddenCount === 0) { return null } + // Why: the visible count summary also needs locale-specific pluralization and word order. const lineText = placement === 'pinned-fallback' - ? `Hiding ${hiddenCount} discovered ${worktreeNoun} in ${repoDisplayName}` - : `Hiding ${hiddenCount} discovered ${worktreeNoun}` + ? hiddenCount === 1 + ? translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.hidingOneInRepo', + 'Hiding 1 discovered worktree in {{value0}}', + { value0: repoDisplayName } + ) + : translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.hidingManyInRepo', + 'Hiding {{count}} discovered worktrees in {{value0}}', + { count: hiddenCount, value0: repoDisplayName } + ) + : hiddenCount === 1 + ? translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.hidingOne', + 'Hiding 1 discovered worktree' + ) + : translate( + 'auto.components.sidebar.ImportedWorktreesVisibilityLine.hidingMany', + 'Hiding {{count}} discovered worktrees', + { count: hiddenCount } + ) const toggleGroupExpanded = (path: string): void => { const key = normalizeRuntimePathForComparison(path) @@ -130,11 +171,7 @@ export default function ImportedWorktreesVisibilityLine({ size="icon-xs" disabled={pending} aria-expanded={isExpanded} - aria-label={translate( - 'auto.components.sidebar.ImportedWorktreesVisibilityLine.f54f2bec5d', - '{{value0}} hidden worktrees for {{value1}}', - { value0: isExpanded ? 'Collapse' : 'Expand', value1: repoDisplayName } - )} + aria-label={hiddenWorktreeDisclosureLabel} onClick={() => setIsExpanded((value) => !value)} className="shrink-0 rounded-[4px] text-muted-foreground hover:bg-worktree-sidebar-accent hover:text-worktree-sidebar-accent-foreground" > @@ -160,7 +197,7 @@ export default function ImportedWorktreesVisibilityLine({ - {KEEP_HIDDEN_LABEL} + {keepHiddenLabel} ) : null} diff --git a/src/renderer/src/components/sidebar/SidebarWorkspaceOptionsMenu.tsx b/src/renderer/src/components/sidebar/SidebarWorkspaceOptionsMenu.tsx index fcfbfb9ed952..31daa0adc667 100644 --- a/src/renderer/src/components/sidebar/SidebarWorkspaceOptionsMenu.tsx +++ b/src/renderer/src/components/sidebar/SidebarWorkspaceOptionsMenu.tsx @@ -107,7 +107,7 @@ const SidebarWorkspaceOptionsMenu = React.memo(function SidebarWorkspaceOptionsM aria-label={ hasAnyFilter ? translate( - 'auto.components.sidebar.SidebarWorkspaceOptionsMenu.bc96dbd041', + 'auto.components.sidebar.SidebarWorkspaceOptionsMenu.optionsWithActiveFilter', 'Workspace options ({{value0}} active)', { value0: activeFilterLabel } ) diff --git a/src/renderer/src/components/sidebar/WorkspaceKanbanSettingsMenu.tsx b/src/renderer/src/components/sidebar/WorkspaceKanbanSettingsMenu.tsx index a7fd46931437..a5122b3f6bf7 100644 --- a/src/renderer/src/components/sidebar/WorkspaceKanbanSettingsMenu.tsx +++ b/src/renderer/src/components/sidebar/WorkspaceKanbanSettingsMenu.tsx @@ -164,7 +164,7 @@ export default function WorkspaceKanbanSettingsMenu({ disabled={index === workspaceStatuses.length - 1} onClick={() => onMoveStatus(status.id, 1)} aria-label={translate( - 'auto.components.sidebar.WorkspaceKanbanSettingsMenu.b45b350eb0', + 'auto.components.sidebar.WorkspaceKanbanSettingsMenu.moveRight', 'Move {{value0}} right', { value0: status.label } )} diff --git a/src/renderer/src/components/sidebar/WorkspaceStatusAppearancePopover.tsx b/src/renderer/src/components/sidebar/WorkspaceStatusAppearancePopover.tsx index c018d7ae048a..2acfe9cd3758 100644 --- a/src/renderer/src/components/sidebar/WorkspaceStatusAppearancePopover.tsx +++ b/src/renderer/src/components/sidebar/WorkspaceStatusAppearancePopover.tsx @@ -108,7 +108,7 @@ export default function WorkspaceStatusAppearancePopover({ className="size-8" onClick={() => onChangeIcon(status.id, icon.id)} aria-label={translate( - 'auto.components.sidebar.WorkspaceStatusAppearancePopover.514be2f569', + 'auto.components.sidebar.WorkspaceStatusAppearancePopover.setIcon', 'Set {{value0}} icon to {{value1}}', { value0: status.label, value1: icon.label } )} diff --git a/src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx b/src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx index 0a8bb4d248e2..22ff664fd4a2 100644 --- a/src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx @@ -40,6 +40,18 @@ const port: WorkspacePort = { } describe('WorktreeCardPortsDetails', () => { + it('localizes complete singular and plural trigger labels', async () => { + const { WorktreeCardPortsTrigger } = await import('./WorktreeCardPorts') + const secondPort = { ...port, id: '127.0.0.1:58942:1235', port: 58942, pid: 1235 } + + expect(renderToStaticMarkup()).toContain( + 'aria-label="1 live port"' + ) + expect(renderToStaticMarkup()).toContain( + 'aria-label="2 live ports"' + ) + }) + it('shows advertised port addresses in workspace hover details', async () => { const { WorktreeCardPortsDetails } = await import('./WorktreeCardPorts') diff --git a/src/renderer/src/components/sidebar/WorktreeCardPorts.tsx b/src/renderer/src/components/sidebar/WorktreeCardPorts.tsx index abeea1fda54e..7e3f9b560951 100644 --- a/src/renderer/src/components/sidebar/WorktreeCardPorts.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCardPorts.tsx @@ -39,15 +39,23 @@ export function WorktreeCardPortsTrigger({ return null } + // Why: CJK pluralization cannot be expressed by interpolating the English words port/ports. + const livePortsLabel = + ports.length === 1 + ? translate('auto.components.sidebar.WorktreeCardPorts.oneLivePort', '{{count}} live port', { + count: ports.length + }) + : translate( + 'auto.components.sidebar.WorktreeCardPorts.manyLivePorts', + '{{count}} live ports', + { count: ports.length } + ) + return (