Skip to content

Commit

Permalink
Localize lang selectors according to the app language (#6207)
Browse files Browse the repository at this point in the history
* Localize lang selectors according to the app language

* Explicitly ignore RangeError when translating locale names
  • Loading branch information
Signez authored Dec 31, 2024
1 parent 09297d9 commit 9f075b1
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 45 deletions.
42 changes: 39 additions & 3 deletions src/locale/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion src/locale/languages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
interface Language {
export interface Language {
code3: string
code2: string
name: string
Expand Down
8 changes: 4 additions & 4 deletions src/screens/Settings/LanguageSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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,
}))}
Expand Down
6 changes: 4 additions & 2 deletions src/view/com/composer/select-language/SelectLangBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(' + ')

/*
Expand Down Expand Up @@ -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
Expand Down
69 changes: 40 additions & 29 deletions src/view/com/composer/select-language/SuggestedLanguage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}}
Expand Down
3 changes: 2 additions & 1 deletion src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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={[
Expand Down
9 changes: 5 additions & 4 deletions src/view/screens/Search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -328,7 +329,7 @@ function SearchLanguageDropdown({
}) {
const t = useThemeNew()
const {_} = useLingui()
const {contentLanguages} = useLanguagePrefs()
const {appLanguage, contentLanguages} = useLanguagePrefs()

const items = React.useMemo(() => {
return [
Expand All @@ -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,
}))
Expand All @@ -365,7 +366,7 @@ function SearchLanguageDropdown({
return a.label.localeCompare(b.label)
}),
)
}, [_, contentLanguages])
}, [_, appLanguage, contentLanguages])

const style = {
backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
Expand Down

0 comments on commit 9f075b1

Please sign in to comment.