mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
fix: address review findings (#5088)
This commit is contained in:
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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: '닫기',
|
||||
|
||||
@@ -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(', ')}`)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 }))
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
@@ -16,7 +16,8 @@
|
||||
"english": "English",
|
||||
"chinese": "中文(简体)",
|
||||
"korean": "한국어",
|
||||
"japanese": "日本語"
|
||||
"japanese": "日本語",
|
||||
"spanish": "Español"
|
||||
},
|
||||
"statusBar": {
|
||||
"claudeToggleDescription": "アクティブなワークスペースのClaude トークンとコストの使用状況を表示します。",
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"english": "English",
|
||||
"chinese": "中文(简体)",
|
||||
"korean": "한국어",
|
||||
"japanese": "日本語"
|
||||
"japanese": "日本語",
|
||||
"spanish": "Español"
|
||||
},
|
||||
"statusBar": {
|
||||
"claudeToggleDescription": "활성 워크스페이스에 대한 Claude 토큰 및 비용 사용량을 표시합니다.",
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"english": "English",
|
||||
"chinese": "中文(简体)",
|
||||
"korean": "한국어",
|
||||
"japanese": "日本語"
|
||||
"japanese": "日本語",
|
||||
"spanish": "Español"
|
||||
},
|
||||
"statusBar": {
|
||||
"claudeToggleDescription": "显示活动工作区的 Claude Token 和成本使用情况。",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user