Skip to content

Commit 4a6d5dc

Browse files
committed
Fix language preference fallback display
1 parent d2a4b41 commit 4a6d5dc

3 files changed

Lines changed: 88 additions & 12 deletions

File tree

src/renderer/components/Pref/General.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { agent } from '@renderer/core/agent'
1111
import useI18n from '@renderer/models/useI18n'
1212
import { normalizeTheme } from '@renderer/utils/theme'
1313
import type { CSSProperties } from 'react'
14+
import { languageOptions, resolveLanguageSelectValue } from './languageOptions'
1415

1516
interface IProps {
1617
data: ConfigsType
@@ -26,7 +27,7 @@ const segmentedControlLabelStyle: CSSProperties = {
2627

2728
const General = (props: IProps) => {
2829
const { data, onChange } = props
29-
const { lang } = useI18n()
30+
const { lang, locale } = useI18n()
3031
const { platform } = agent
3132

3233
return (
@@ -42,18 +43,9 @@ const General = (props: IProps) => {
4243
>
4344
<Box>{lang.language}</Box>
4445
<Select
45-
value={data.locale}
46+
value={resolveLanguageSelectValue(data.locale, locale)}
4647
onChange={(v) => v && onChange({ locale: v as LocaleName })}
47-
data={[
48-
{ value: 'zh', label: '简体中文' },
49-
{ value: 'zh_hant', label: '繁體中文' },
50-
{ value: 'en', label: 'English' },
51-
{ value: 'fr', label: 'Français' },
52-
{ value: 'de', label: 'Deutsch' },
53-
{ value: 'ja', label: '日本語' },
54-
{ value: 'tr', label: 'Türkçe' },
55-
{ value: 'ko', label: '한국어' },
56-
]}
48+
data={languageOptions}
5749
w={200}
5850
allowDeselect={false}
5951
/>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
import {
4+
languageOptions,
5+
normalizeLanguageOptionValue,
6+
resolveLanguageSelectValue,
7+
} from './languageOptions'
8+
9+
describe('language preference options', () => {
10+
it('shows the active system language when no locale is saved', () => {
11+
expect(resolveLanguageSelectValue(undefined, 'zh-CN')).toBe('zh')
12+
})
13+
14+
it('normalizes locale aliases to selectable option values', () => {
15+
expect(normalizeLanguageOptionValue('cn')).toBe('zh')
16+
expect(normalizeLanguageOptionValue('zh-CN')).toBe('zh')
17+
expect(normalizeLanguageOptionValue('zh-TW')).toBe('zh_hant')
18+
})
19+
20+
it('prefers a saved language over the active system language', () => {
21+
expect(resolveLanguageSelectValue('de', 'zh-CN')).toBe('de')
22+
})
23+
24+
it('lists every canonical bundled language', () => {
25+
expect(languageOptions.map(({ value }) => value)).toEqual([
26+
'zh',
27+
'zh_hant',
28+
'en',
29+
'fr',
30+
'de',
31+
'ja',
32+
'tr',
33+
'ko',
34+
'pl',
35+
])
36+
})
37+
})
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { LocaleName } from '@common/i18n'
2+
3+
export const languageOptions = [
4+
{ value: 'zh', label: '简体中文' },
5+
{ value: 'zh_hant', label: '繁體中文' },
6+
{ value: 'en', label: 'English' },
7+
{ value: 'fr', label: 'Français' },
8+
{ value: 'de', label: 'Deutsch' },
9+
{ value: 'ja', label: '日本語' },
10+
{ value: 'tr', label: 'Türkçe' },
11+
{ value: 'ko', label: '한국어' },
12+
{ value: 'pl', label: 'Polski' },
13+
] as const satisfies ReadonlyArray<{ value: LocaleName; label: string }>
14+
15+
type LanguageOptionValue = (typeof languageOptions)[number]['value']
16+
17+
const languageOptionValues = new Set<LocaleName>(
18+
languageOptions.map(({ value }) => value),
19+
)
20+
21+
const localeAliases: Partial<Record<LocaleName, LanguageOptionValue>> = {
22+
cn: 'zh',
23+
'zh-CN': 'zh',
24+
'zh-TW': 'zh_hant',
25+
}
26+
27+
export function normalizeLanguageOptionValue(
28+
locale?: LocaleName,
29+
): LanguageOptionValue | undefined {
30+
if (!locale) return undefined
31+
32+
const alias = localeAliases[locale]
33+
if (alias) return alias
34+
35+
return languageOptionValues.has(locale) ? (locale as LanguageOptionValue) : undefined
36+
}
37+
38+
export function resolveLanguageSelectValue(
39+
configLocale: LocaleName | undefined,
40+
activeLocale: LocaleName,
41+
): LanguageOptionValue {
42+
return (
43+
normalizeLanguageOptionValue(configLocale) ??
44+
normalizeLanguageOptionValue(activeLocale) ??
45+
'en'
46+
)
47+
}

0 commit comments

Comments
 (0)