fix: address review findings (#5088)

This commit is contained in:
Jinjing
2026-06-11 12:35:04 -07:00
committed by Jinwoo-H
parent 1194ca3995
commit 4ebbecf578
19 changed files with 10065 additions and 14 deletions
+1
View File
@@ -110,3 +110,4 @@ validation-screenshots/
src/renderer/src/i18n/locales/.zh-catalog-cache.json
src/renderer/src/i18n/locales/.ko-catalog-cache.json
src/renderer/src/i18n/locales/.ja-catalog-cache.json
src/renderer/src/i18n/locales/.es-catalog-cache.json
@@ -29,6 +29,11 @@ const LOCALE_CONFIG = {
targetLanguage: 'ja',
displayName: 'Japanese',
cacheFile: '.ja-catalog-cache.json'
},
es: {
targetLanguage: 'es',
displayName: 'Spanish',
cacheFile: '.es-catalog-cache.json'
}
}
+17 -3
View File
@@ -125,13 +125,27 @@ export const BRAND_MISTRANSLATIONS = {
'GitHub Copilot': ['GitHub コパイロット', 'コパイロット'],
Discord: ['不和'],
Linear: ['線形']
},
es: {
Codex: ['códice', 'Códice'],
Gemini: ['Géminis'],
Claude: ['claudia', 'Claudia'],
Orca: ['orca', 'Orcas', 'orcas'],
OpenCode: ['código abierto', 'Código abierto'],
OpenClaude: ['Openclaude'],
Antigravity: ['antigravedad', 'Antigravedad'],
'GitHub Copilot': ['Copiloto de GitHub'],
Discord: ['discordia'],
Linear: ['lineal', 'Lineal'],
Jira: ['jira']
}
}
export const NATIVE_PICKER_LABELS = {
zh: { chinese: '中文(简体)', korean: '한국어', japanese: '日本語' },
ko: { chinese: '中文(简体)', korean: '한국어', japanese: '日本語' },
ja: { chinese: '中文(简体)', korean: '한국어', japanese: '日本語' }
zh: { chinese: '中文(简体)', korean: '한국어', japanese: '日本語', spanish: 'Español' },
ko: { chinese: '中文(简体)', korean: '한국어', japanese: '日本語', spanish: 'Español' },
ja: { chinese: '中文(简体)', korean: '한국어', japanese: '日本語', spanish: 'Español' },
es: { chinese: '中文(简体)', korean: '한국어', japanese: '日本語', spanish: 'Español' }
}
export function isEnglishOnlyKey(key) {
@@ -3,6 +3,11 @@ import { KO_VALUE_OVERRIDES } from './locale-ko-value-overrides.mjs'
import { ZH_VALUE_OVERRIDES } from './locale-zh-value-overrides.mjs'
export const LOCALE_VALUE_OVERRIDES = {
es: {
'Explore Orca': 'Explorar Orca',
'OpenCode Go': 'OpenCode Go',
'Open in Cursor': 'Abrir en Cursor'
},
ko: {
Save: '저장',
Close: '닫기',
+3 -2
View File
@@ -10,7 +10,8 @@ const LOCALES_DIR = path.join('src', 'renderer', 'src', 'i18n', 'locales')
const LOCALE_CACHE_FILES = {
ko: '.ko-catalog-cache.json',
zh: '.zh-catalog-cache.json',
ja: '.ja-catalog-cache.json'
ja: '.ja-catalog-cache.json',
es: '.es-catalog-cache.json'
}
function parseLocaleArg(argv) {
@@ -56,7 +57,7 @@ export async function repairLocale(root, locale) {
}
export async function main(root = process.cwd(), locale = parseLocaleArg(process.argv)) {
const locales = locale ? [locale] : ['ko', 'zh', 'ja']
const locales = locale ? [locale] : ['ko', 'zh', 'ja', 'es']
const unsupported = locales.filter((code) => !LOCALE_CACHE_FILES[code])
if (unsupported.length > 0) {
console.error(`Unsupported locale(s): ${unsupported.join(', ')}`)
+1
View File
@@ -49,6 +49,7 @@
"bootstrap:zh-catalog": "node config/scripts/bootstrap-zh-catalog.mjs",
"bootstrap:ko-catalog": "node config/scripts/bootstrap-locale-catalog.mjs --locale ko",
"bootstrap:ja-catalog": "node config/scripts/bootstrap-locale-catalog.mjs --locale ja",
"bootstrap:es-catalog": "node config/scripts/bootstrap-locale-catalog.mjs --locale es",
"repair:locale-catalog": "node config/scripts/repair-locale-catalog.mjs",
"verify:localization-coverage": "node config/scripts/audit-localization-coverage.mjs --check",
"audit:localization": "node config/scripts/audit-localization-coverage.mjs",
+4
View File
@@ -2,6 +2,7 @@ import { app } from 'electron'
import i18next, { type i18n as I18nInstance, type TOptions } from 'i18next'
import en from '../../renderer/src/i18n/locales/en.json'
import es from '../../renderer/src/i18n/locales/es.json'
import ja from '../../renderer/src/i18n/locales/ja.json'
import ko from '../../renderer/src/i18n/locales/ko.json'
import zh from '../../renderer/src/i18n/locales/zh.json'
@@ -38,6 +39,9 @@ export async function ensureMainI18n(): Promise<I18nInstance> {
},
ja: {
translation: ja
},
es: {
translation: es
}
},
interpolation: {
@@ -143,7 +143,7 @@ describe('AppearancePane', () => {
mocks.state.settingsSearchQuery = 'automations'
})
it('renders the language dropdown with system, english, chinese, korean, and japanese options', async () => {
it('renders the language dropdown with system, english, chinese, korean, japanese, and spanish options', async () => {
mocks.state.settingsSearchQuery = 'language'
const updateSettings = vi.fn()
const settings = {
@@ -166,6 +166,7 @@ describe('AppearancePane', () => {
expect(container.textContent).toContain('中文(简体)')
expect(container.textContent).toContain('한국어')
expect(container.textContent).toContain('日本語')
expect(container.textContent).toContain('Español')
await act(async () => {
chineseOption?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
+4
View File
@@ -2,6 +2,7 @@ import i18next, { type i18n as I18nInstance, type TOptions } from 'i18next'
import { initReactI18next } from 'react-i18next'
import en from './locales/en.json'
import es from './locales/es.json'
import ja from './locales/ja.json'
import ko from './locales/ko.json'
import zh from './locales/zh.json'
@@ -26,6 +27,9 @@ void i18n.use(initReactI18next).init({
},
ja: {
translation: ja
},
es: {
translation: es
}
},
interpolation: {
+2 -1
View File
@@ -16,7 +16,8 @@
"english": "English",
"chinese": "中文(简体)",
"korean": "한국어",
"japanese": "日本語"
"japanese": "日本語",
"spanish": "Español"
},
"statusBar": {
"claudeToggleDescription": "Show Claude token and cost usage for the active workspace.",
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -16,7 +16,8 @@
"english": "English",
"chinese": "中文(简体)",
"korean": "한국어",
"japanese": "日本語"
"japanese": "日本語",
"spanish": "Español"
},
"statusBar": {
"claudeToggleDescription": "アクティブなワークスペースのClaude トークンとコストの使用状況を表示します。",
+2 -1
View File
@@ -16,7 +16,8 @@
"english": "English",
"chinese": "中文(简体)",
"korean": "한국어",
"japanese": "日本語"
"japanese": "日本語",
"spanish": "Español"
},
"statusBar": {
"claudeToggleDescription": "활성 워크스페이스에 대한 Claude 토큰 및 비용 사용량을 표시합니다.",
+2 -1
View File
@@ -16,7 +16,8 @@
"english": "English",
"chinese": "中文(简体)",
"korean": "한국어",
"japanese": "日本語"
"japanese": "日本語",
"spanish": "Español"
},
"statusBar": {
"claudeToggleDescription": "显示活动工作区的 Claude Token 和成本使用情况。",
+5 -2
View File
@@ -8,6 +8,7 @@ import {
UI_LANGUAGE_ENGLISH,
UI_LANGUAGE_JAPANESE,
UI_LANGUAGE_KOREAN,
UI_LANGUAGE_SPANISH,
UI_LANGUAGE_SYSTEM,
type UiLanguage
} from '../../../shared/ui-language'
@@ -26,7 +27,8 @@ export const UI_LANGUAGE_CHOICES: UiLanguageChoice[] = [
{ value: UI_LANGUAGE_ENGLISH, labelKey: 'settings.appearance.language.english' },
{ value: UI_LANGUAGE_CHINESE, labelKey: 'settings.appearance.language.chinese' },
{ value: UI_LANGUAGE_KOREAN, labelKey: 'settings.appearance.language.korean' },
{ value: UI_LANGUAGE_JAPANESE, labelKey: 'settings.appearance.language.japanese' }
{ value: UI_LANGUAGE_JAPANESE, labelKey: 'settings.appearance.language.japanese' },
{ value: UI_LANGUAGE_SPANISH, labelKey: 'settings.appearance.language.spanish' }
]
const UI_LANGUAGE_CHOICE_FALLBACKS: Record<UiLanguage, string> = {
@@ -34,7 +36,8 @@ const UI_LANGUAGE_CHOICE_FALLBACKS: Record<UiLanguage, string> = {
[UI_LANGUAGE_ENGLISH]: 'English',
[UI_LANGUAGE_CHINESE]: '中文(简体)',
[UI_LANGUAGE_KOREAN]: '한국어',
[UI_LANGUAGE_JAPANESE]: '日本語'
[UI_LANGUAGE_JAPANESE]: '日本語',
[UI_LANGUAGE_SPANISH]: 'Español'
}
export function getUiLanguageChoiceLabel(
+2
View File
@@ -5,6 +5,7 @@ import {
UI_LANGUAGE_ENGLISH,
UI_LANGUAGE_JAPANESE,
UI_LANGUAGE_KOREAN,
UI_LANGUAGE_SPANISH,
UI_LANGUAGE_SYSTEM,
normalizeUiLanguage
} from './ui-language'
@@ -16,6 +17,7 @@ describe('normalizeUiLanguage', () => {
expect(normalizeUiLanguage(UI_LANGUAGE_CHINESE)).toBe('zh')
expect(normalizeUiLanguage(UI_LANGUAGE_KOREAN)).toBe('ko')
expect(normalizeUiLanguage(UI_LANGUAGE_JAPANESE)).toBe('ja')
expect(normalizeUiLanguage(UI_LANGUAGE_SPANISH)).toBe('es')
})
it('falls back unknown values to system', () => {
+4 -1
View File
@@ -3,6 +3,7 @@ export const UI_LANGUAGE_ENGLISH = 'en'
export const UI_LANGUAGE_CHINESE = 'zh'
export const UI_LANGUAGE_KOREAN = 'ko'
export const UI_LANGUAGE_JAPANESE = 'ja'
export const UI_LANGUAGE_SPANISH = 'es'
export type UiLanguage =
| typeof UI_LANGUAGE_SYSTEM
@@ -10,13 +11,15 @@ export type UiLanguage =
| typeof UI_LANGUAGE_CHINESE
| typeof UI_LANGUAGE_KOREAN
| typeof UI_LANGUAGE_JAPANESE
| typeof UI_LANGUAGE_SPANISH
const UI_LANGUAGE_VALUES = new Set<UiLanguage>([
UI_LANGUAGE_SYSTEM,
UI_LANGUAGE_ENGLISH,
UI_LANGUAGE_CHINESE,
UI_LANGUAGE_KOREAN,
UI_LANGUAGE_JAPANESE
UI_LANGUAGE_JAPANESE,
UI_LANGUAGE_SPANISH
])
export function normalizeUiLanguage(value: unknown): UiLanguage {
+13
View File
@@ -6,6 +6,7 @@ import {
UI_LANGUAGE_ENGLISH,
UI_LANGUAGE_JAPANESE,
UI_LANGUAGE_KOREAN,
UI_LANGUAGE_SPANISH,
UI_LANGUAGE_SYSTEM
} from './ui-language'
@@ -27,6 +28,12 @@ describe('ui-locale', () => {
expect(normalizeSupportedUiLocale('ja')).toBe('ja')
})
it('normalizes Spanish locale prefixes', () => {
expect(normalizeSupportedUiLocale('es-ES')).toBe('es')
expect(normalizeSupportedUiLocale('es-MX')).toBe('es')
expect(normalizeSupportedUiLocale('es')).toBe('es')
})
it('falls back unsupported locales to English', () => {
expect(normalizeSupportedUiLocale('fr-FR')).toBe('en')
})
@@ -53,11 +60,16 @@ describe('ui-locale', () => {
expect(resolveUiLocale(UI_LANGUAGE_JAPANESE, 'en-US')).toBe('ja')
})
it('resolves explicit Spanish independently of system locale', () => {
expect(resolveUiLocale(UI_LANGUAGE_SPANISH, 'en-US')).toBe('es')
})
it('maps system locale to the closest supported locale', () => {
expect(resolveUiLocale(UI_LANGUAGE_SYSTEM, 'en-GB')).toBe('en')
expect(resolveUiLocale(UI_LANGUAGE_SYSTEM, 'zh-CN')).toBe('zh')
expect(resolveUiLocale(UI_LANGUAGE_SYSTEM, 'ko-KR')).toBe('ko')
expect(resolveUiLocale(UI_LANGUAGE_SYSTEM, 'ja-JP')).toBe('ja')
expect(resolveUiLocale(UI_LANGUAGE_SYSTEM, 'es-MX')).toBe('es')
expect(resolveUiLocale(UI_LANGUAGE_SYSTEM, 'fr-FR')).toBe('en')
})
@@ -66,5 +78,6 @@ describe('ui-locale', () => {
expect(resolveRendererUiLocale(UI_LANGUAGE_CHINESE)).toBe('zh')
expect(resolveRendererUiLocale(UI_LANGUAGE_KOREAN)).toBe('ko')
expect(resolveRendererUiLocale(UI_LANGUAGE_JAPANESE)).toBe('ja')
expect(resolveRendererUiLocale(UI_LANGUAGE_SPANISH)).toBe('es')
})
})
+5 -1
View File
@@ -3,11 +3,12 @@ import {
UI_LANGUAGE_ENGLISH,
UI_LANGUAGE_JAPANESE,
UI_LANGUAGE_KOREAN,
UI_LANGUAGE_SPANISH,
UI_LANGUAGE_SYSTEM,
type UiLanguage
} from './ui-language'
export const SUPPORTED_UI_LOCALES = ['en', 'zh', 'ko', 'ja'] as const
export const SUPPORTED_UI_LOCALES = ['en', 'zh', 'ko', 'ja', 'es'] as const
export type SupportedUiLocale = (typeof SUPPORTED_UI_LOCALES)[number]
export const DEFAULT_UI_LOCALE: SupportedUiLocale = 'en'
@@ -46,6 +47,9 @@ export function resolveUiLocale(
if (language === UI_LANGUAGE_JAPANESE) {
return 'ja'
}
if (language === UI_LANGUAGE_SPANISH) {
return 'es'
}
return normalizeSupportedUiLocale(systemLocale)
}