Localize lang selectors according to the app language (#6207)
* Localize lang selectors according to the app language * Explicitly ignore RangeError when translating locale names
This commit is contained in:
committed by
GitHub
parent
09297d92cb
commit
9f075b1340
+39
-3
@@ -5,6 +5,7 @@ import lande from 'lande'
|
||||
import {hasProp} from '#/lib/type-guards'
|
||||
import {
|
||||
AppLanguage,
|
||||
type Language,
|
||||
LANGUAGES_MAP_CODE2,
|
||||
LANGUAGES_MAP_CODE3,
|
||||
} from './languages'
|
||||
@@ -31,9 +32,44 @@ export function code3ToCode2Strict(lang: string): string | undefined {
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function codeToLanguageName(lang: string): string {
|
||||
const lang2 = code3ToCode2(lang)
|
||||
return LANGUAGES_MAP_CODE2[lang2]?.name || lang
|
||||
function getLocalizedLanguage(
|
||||
langCode: string,
|
||||
appLang: string,
|
||||
): string | undefined {
|
||||
try {
|
||||
const allNames = new Intl.DisplayNames([appLang], {
|
||||
type: 'language',
|
||||
fallback: 'none',
|
||||
languageDisplay: 'standard',
|
||||
})
|
||||
const translatedName = allNames.of(langCode)
|
||||
|
||||
if (translatedName) {
|
||||
// force simple title case (as languages do not always start with an uppercase in Unicode data)
|
||||
return translatedName[0].toLocaleUpperCase() + translatedName.slice(1)
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore RangeError from Intl.DisplayNames APIs
|
||||
if (!(e instanceof RangeError)) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function languageName(language: Language, appLang: string): string {
|
||||
// if Intl.DisplayNames is unavailable on the target, display the English name
|
||||
if (!(Intl as any).DisplayNames) {
|
||||
return language.name
|
||||
}
|
||||
|
||||
return getLocalizedLanguage(language.code2, appLang) || language.name
|
||||
}
|
||||
|
||||
export function codeToLanguageName(lang2or3: string, appLang: string): string {
|
||||
const code2 = code3ToCode2(lang2or3)
|
||||
const knownLanguage = LANGUAGES_MAP_CODE2[code2]
|
||||
|
||||
return knownLanguage ? languageName(knownLanguage, appLang) : code2
|
||||
}
|
||||
|
||||
export function getPostLanguage(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
interface Language {
|
||||
export interface Language {
|
||||
code3: string
|
||||
code2: string
|
||||
name: string
|
||||
|
||||
@@ -6,7 +6,7 @@ import {useLingui} from '@lingui/react'
|
||||
|
||||
import {APP_LANGUAGES, LANGUAGES} from '#/lib/../locale/languages'
|
||||
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
|
||||
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
||||
import {languageName, sanitizeAppLanguageSetting} from '#/locale/helpers'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
@@ -57,10 +57,10 @@ export function LanguageSettingsScreen({}: Props) {
|
||||
.map(lang => LANGUAGES.find(l => l.code2 === lang))
|
||||
.filter(Boolean)
|
||||
// @ts-ignore
|
||||
.map(l => l.name)
|
||||
.map(l => languageName(l, langPrefs.appLanguage))
|
||||
.join(', ')
|
||||
)
|
||||
}, [langPrefs.contentLanguages])
|
||||
}, [langPrefs.appLanguage, langPrefs.contentLanguages])
|
||||
|
||||
return (
|
||||
<Layout.Screen testID="PreferencesLanguagesScreen">
|
||||
@@ -179,7 +179,7 @@ export function LanguageSettingsScreen({}: Props) {
|
||||
value={langPrefs.primaryLanguage}
|
||||
onValueChange={onChangePrimaryLanguage}
|
||||
items={LANGUAGES.filter(l => Boolean(l.code2)).map(l => ({
|
||||
label: l.name,
|
||||
label: languageName(l, langPrefs.appLanguage),
|
||||
value: l.code2,
|
||||
key: l.code2 + l.code3,
|
||||
}))}
|
||||
|
||||
@@ -48,7 +48,7 @@ export function SelectLangBtn() {
|
||||
function add(commaSeparatedLangCodes: string) {
|
||||
const langCodes = commaSeparatedLangCodes.split(',')
|
||||
const langName = langCodes
|
||||
.map(code => codeToLanguageName(code))
|
||||
.map(code => codeToLanguageName(code, langPrefs.appLanguage))
|
||||
.join(' + ')
|
||||
|
||||
/*
|
||||
@@ -108,7 +108,9 @@ export function SelectLangBtn() {
|
||||
accessibilityHint="">
|
||||
{postLanguagesPref.length > 0 ? (
|
||||
<Text type="lg-bold" style={[pal.link, styles.label]} numberOfLines={1}>
|
||||
{postLanguagesPref.map(lang => codeToLanguageName(lang)).join(', ')}
|
||||
{postLanguagesPref
|
||||
.map(lang => codeToLanguageName(lang, langPrefs.appLanguage))
|
||||
.join(', ')}
|
||||
</Text>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
|
||||
@@ -49,37 +49,48 @@ export function SuggestedLanguage({text}: {text: string}) {
|
||||
return () => cancelIdle(idle)
|
||||
}, [text])
|
||||
|
||||
return suggestedLanguage &&
|
||||
!toPostLanguages(langPrefs.postLanguage).includes(suggestedLanguage) ? (
|
||||
<View style={[pal.border, styles.infoBar]}>
|
||||
<FontAwesomeIcon
|
||||
icon="language"
|
||||
style={pal.text as FontAwesomeIconStyle}
|
||||
size={24}
|
||||
/>
|
||||
<Text style={[pal.text, s.flex1]}>
|
||||
<Trans>
|
||||
Are you writing in{' '}
|
||||
<Text type="sm-bold" style={pal.text}>
|
||||
{codeToLanguageName(suggestedLanguage)}
|
||||
</Text>
|
||||
?
|
||||
</Trans>
|
||||
</Text>
|
||||
if (
|
||||
suggestedLanguage &&
|
||||
!toPostLanguages(langPrefs.postLanguage).includes(suggestedLanguage)
|
||||
) {
|
||||
const suggestedLanguageName = codeToLanguageName(
|
||||
suggestedLanguage,
|
||||
langPrefs.appLanguage,
|
||||
)
|
||||
|
||||
<Button
|
||||
type="default"
|
||||
onPress={() => setLangPrefs.setPostLanguage(suggestedLanguage)}
|
||||
accessibilityLabel={_(
|
||||
msg`Change post language to ${codeToLanguageName(suggestedLanguage)}`,
|
||||
)}
|
||||
accessibilityHint="">
|
||||
<Text type="button" style={[pal.link, s.fw600]}>
|
||||
<Trans>Yes</Trans>
|
||||
return (
|
||||
<View style={[pal.border, styles.infoBar]}>
|
||||
<FontAwesomeIcon
|
||||
icon="language"
|
||||
style={pal.text as FontAwesomeIconStyle}
|
||||
size={24}
|
||||
/>
|
||||
<Text style={[pal.text, s.flex1]}>
|
||||
<Trans>
|
||||
Are you writing in{' '}
|
||||
<Text type="sm-bold" style={pal.text}>
|
||||
{suggestedLanguageName}
|
||||
</Text>
|
||||
?
|
||||
</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
</View>
|
||||
) : null
|
||||
|
||||
<Button
|
||||
type="default"
|
||||
onPress={() => setLangPrefs.setPostLanguage(suggestedLanguage)}
|
||||
accessibilityLabel={_(
|
||||
msg`Change post language to ${suggestedLanguageName}`,
|
||||
)}
|
||||
accessibilityHint="">
|
||||
<Text type="button" style={[pal.link, s.fw600]}>
|
||||
<Trans>Yes</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
</View>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -5,6 +5,7 @@ import {Trans} from '@lingui/macro'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {deviceLanguageCodes} from '#/locale/deviceLocales'
|
||||
import {languageName} from '#/locale/helpers'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
useLanguagePrefs,
|
||||
@@ -88,7 +89,7 @@ export function Component({}: {}) {
|
||||
key={lang.code2}
|
||||
code2={lang.code2}
|
||||
langType="contentLanguages"
|
||||
name={lang.name}
|
||||
name={languageName(lang, langPrefs.appLanguage)}
|
||||
onPress={() => {
|
||||
onPress(lang.code2)
|
||||
}}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {Trans} from '@lingui/macro'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {deviceLanguageCodes} from '#/locale/deviceLocales'
|
||||
import {languageName} from '#/locale/helpers'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {
|
||||
hasPostLanguage,
|
||||
@@ -91,7 +92,7 @@ export function Component() {
|
||||
return (
|
||||
<ToggleButton
|
||||
key={lang.code2}
|
||||
label={lang.name}
|
||||
label={languageName(lang, langPrefs.appLanguage)}
|
||||
isSelected={isSelected}
|
||||
onPress={() => (isDisabled ? undefined : onPress(lang.code2))}
|
||||
style={[
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
} from '#/lib/routes/types'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {augmentSearchQuery} from '#/lib/strings/helpers'
|
||||
import {languageName} from '#/locale/helpers'
|
||||
import {logger} from '#/logger'
|
||||
import {isNative, isWeb} from '#/platform/detection'
|
||||
import {listenSoftReset} from '#/state/events'
|
||||
@@ -328,7 +329,7 @@ function SearchLanguageDropdown({
|
||||
}) {
|
||||
const t = useThemeNew()
|
||||
const {_} = useLingui()
|
||||
const {contentLanguages} = useLanguagePrefs()
|
||||
const {appLanguage, contentLanguages} = useLanguagePrefs()
|
||||
|
||||
const items = React.useMemo(() => {
|
||||
return [
|
||||
@@ -345,8 +346,8 @@ function SearchLanguageDropdown({
|
||||
index === self.findIndex(t => t.code2 === lang.code2), // remove dupes (which will happen)
|
||||
)
|
||||
.map(l => ({
|
||||
label: l.name,
|
||||
inputLabel: l.name,
|
||||
label: languageName(l, appLanguage),
|
||||
inputLabel: languageName(l, appLanguage),
|
||||
value: l.code2,
|
||||
key: l.code2 + l.code3,
|
||||
}))
|
||||
@@ -365,7 +366,7 @@ function SearchLanguageDropdown({
|
||||
return a.label.localeCompare(b.label)
|
||||
}),
|
||||
)
|
||||
}, [_, contentLanguages])
|
||||
}, [_, appLanguage, contentLanguages])
|
||||
|
||||
const style = {
|
||||
backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
|
||||
|
||||
Reference in New Issue
Block a user