Internationalization & localization (#1822)

* install and setup lingui

* setup dynamic locale activation and async loading

* first pass of automated replacement of text messages

* add some more documentaton

* fix nits

* add `es` and `hi`locales for testing purposes

* make accessibilityLabel localized

* compile and extract new messages

* fix merge conflicts

* fix eslint warning

* change instructions from sending email to opening PR

* fix comments
This commit is contained in:
Ansh 2023-11-09 10:04:16 -08:00 committed by GitHub
parent 82059b7ee1
commit 4c7850f8c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
108 changed files with 10334 additions and 1365 deletions

View file

@ -63,6 +63,8 @@ import {
// -prf
import {useDebugHeaderSetting} from 'lib/api/debug-appview-proxy-header'
import {STATUS_PAGE_URL} from 'lib/constants'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
export const SettingsScreen = withAuthRequired(
@ -71,6 +73,7 @@ export const SettingsScreen = withAuthRequired(
const setColorMode = useSetColorMode()
const pal = usePalette('default')
const store = useStores()
const {_} = useLingui()
const setMinimalShellMode = useSetMinimalShellMode()
const requireAltTextEnabled = useRequireAltTextEnabled()
const setRequireAltTextEnabled = useSetRequireAltTextEnabled()
@ -213,7 +216,7 @@ export const SettingsScreen = withAuthRequired(
{store.session.currentSession !== undefined ? (
<>
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Account
<Trans>Account</Trans>
</Text>
<View style={[styles.infoLine]}>
<Text type="lg-medium" style={pal.text}>
@ -233,17 +236,17 @@ export const SettingsScreen = withAuthRequired(
</Text>
<Link onPress={() => openModal({name: 'change-email'})}>
<Text type="lg" style={pal.link}>
Change
<Trans>Change</Trans>
</Text>
</Link>
</View>
<View style={[styles.infoLine]}>
<Text type="lg-medium" style={pal.text}>
Birthday:{' '}
<Trans>Birthday: </Trans>
</Text>
<Link onPress={() => openModal({name: 'birth-date-settings'})}>
<Text type="lg" style={pal.link}>
Show
<Trans>Show</Trans>
</Text>
</Link>
</View>
@ -253,7 +256,7 @@ export const SettingsScreen = withAuthRequired(
) : null}
<View style={[s.flexRow, styles.heading]}>
<Text type="xl-bold" style={pal.text}>
Signed in as
<Trans>Signed in as</Trans>
</Text>
<View style={s.flex1} />
</View>
@ -282,10 +285,10 @@ export const SettingsScreen = withAuthRequired(
testID="signOutBtn"
onPress={isSwitching ? undefined : onPressSignout}
accessibilityRole="button"
accessibilityLabel="Sign out"
accessibilityLabel={_(msg`Sign out`)}
accessibilityHint={`Signs ${store.me.displayName} out of Bluesky`}>
<Text type="lg" style={pal.link}>
Sign out
<Trans>Sign out</Trans>
</Text>
</TouchableOpacity>
</View>
@ -321,7 +324,7 @@ export const SettingsScreen = withAuthRequired(
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
onPress={isSwitching ? undefined : onPressAddAccount}
accessibilityRole="button"
accessibilityLabel="Add account"
accessibilityLabel={_(msg`Add account`)}
accessibilityHint="Create a new Bluesky account">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
@ -330,21 +333,21 @@ export const SettingsScreen = withAuthRequired(
/>
</View>
<Text type="lg" style={pal.text}>
Add account
<Trans>Add account</Trans>
</Text>
</TouchableOpacity>
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Invite a Friend
<Trans>Invite a Friend</Trans>
</Text>
<TouchableOpacity
testID="inviteFriendBtn"
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
onPress={isSwitching ? undefined : onPressInviteCodes}
accessibilityRole="button"
accessibilityLabel="Invite"
accessibilityLabel={_(msg`Invite`)}
accessibilityHint="Opens invite code list">
<View
style={[
@ -371,7 +374,7 @@ export const SettingsScreen = withAuthRequired(
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Accessibility
<Trans>Accessibility</Trans>
</Text>
<View style={[pal.view, styles.toggleCard]}>
<ToggleButton
@ -386,7 +389,7 @@ export const SettingsScreen = withAuthRequired(
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Appearance
<Trans>Appearance</Trans>
</Text>
<View>
<View style={[styles.linkCard, pal.view, styles.selectableBtns]}>
@ -415,7 +418,7 @@ export const SettingsScreen = withAuthRequired(
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Basics
<Trans>Basics</Trans>
</Text>
<TouchableOpacity
testID="preferencesHomeFeedButton"
@ -423,7 +426,7 @@ export const SettingsScreen = withAuthRequired(
onPress={openHomeFeedPreferences}
accessibilityRole="button"
accessibilityHint=""
accessibilityLabel="Opens the home feed preferences">
accessibilityLabel={_(msg`Opens the home feed preferences`)}>
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="sliders"
@ -431,7 +434,7 @@ export const SettingsScreen = withAuthRequired(
/>
</View>
<Text type="lg" style={pal.text}>
Home Feed Preferences
<Trans>Home Feed Preferences</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -440,7 +443,7 @@ export const SettingsScreen = withAuthRequired(
onPress={openThreadsPreferences}
accessibilityRole="button"
accessibilityHint=""
accessibilityLabel="Opens the threads preferences">
accessibilityLabel={_(msg`Opens the threads preferences`)}>
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon={['far', 'comments']}
@ -449,20 +452,20 @@ export const SettingsScreen = withAuthRequired(
/>
</View>
<Text type="lg" style={pal.text}>
Thread Preferences
<Trans>Thread Preferences</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
testID="savedFeedsBtn"
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
accessibilityHint="My Saved Feeds"
accessibilityLabel="Opens screen with all saved feeds"
accessibilityLabel={_(msg`Opens screen with all saved feeds`)}
onPress={onPressSavedFeeds}>
<View style={[styles.iconContainer, pal.btn]}>
<HashtagIcon style={pal.text} size={18} strokeWidth={3} />
</View>
<Text type="lg" style={pal.text}>
My Saved Feeds
<Trans>My Saved Feeds</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -471,7 +474,7 @@ export const SettingsScreen = withAuthRequired(
onPress={isSwitching ? undefined : onPressLanguageSettings}
accessibilityRole="button"
accessibilityHint="Language settings"
accessibilityLabel="Opens configurable language settings">
accessibilityLabel={_(msg`Opens configurable language settings`)}>
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="language"
@ -479,7 +482,7 @@ export const SettingsScreen = withAuthRequired(
/>
</View>
<Text type="lg" style={pal.text}>
Languages
<Trans>Languages</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -490,18 +493,18 @@ export const SettingsScreen = withAuthRequired(
}
accessibilityRole="button"
accessibilityHint=""
accessibilityLabel="Opens moderation settings">
accessibilityLabel={_(msg`Opens moderation settings`)}>
<View style={[styles.iconContainer, pal.btn]}>
<HandIcon style={pal.text} size={18} strokeWidth={6} />
</View>
<Text type="lg" style={pal.text}>
Moderation
<Trans>Moderation</Trans>
</Text>
</TouchableOpacity>
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Advanced
<Trans>Advanced</Trans>
</Text>
<TouchableOpacity
testID="appPasswordBtn"
@ -509,7 +512,7 @@ export const SettingsScreen = withAuthRequired(
onPress={onPressAppPasswords}
accessibilityRole="button"
accessibilityHint="Open app password settings"
accessibilityLabel="Opens the app password settings page">
accessibilityLabel={_(msg`Opens the app password settings page`)}>
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
icon="lock"
@ -517,7 +520,7 @@ export const SettingsScreen = withAuthRequired(
/>
</View>
<Text type="lg" style={pal.text}>
App passwords
<Trans>App passwords</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -525,7 +528,7 @@ export const SettingsScreen = withAuthRequired(
style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]}
onPress={isSwitching ? undefined : onPressChangeHandle}
accessibilityRole="button"
accessibilityLabel="Change handle"
accessibilityLabel={_(msg`Change handle`)}
accessibilityHint="Choose a new Bluesky username or create">
<View style={[styles.iconContainer, pal.btn]}>
<FontAwesomeIcon
@ -534,19 +537,19 @@ export const SettingsScreen = withAuthRequired(
/>
</View>
<Text type="lg" style={pal.text} numberOfLines={1}>
Change handle
<Trans>Change handle</Trans>
</Text>
</TouchableOpacity>
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Danger Zone
<Trans>Danger Zone</Trans>
</Text>
<TouchableOpacity
style={[pal.view, styles.linkCard]}
onPress={onPressDeleteAccount}
accessible={true}
accessibilityRole="button"
accessibilityLabel="Delete account"
accessibilityLabel={_(msg`Delete account`)}
accessibilityHint="Opens modal for account deletion confirmation. Requires email code.">
<View style={[styles.iconContainer, dangerBg]}>
<FontAwesomeIcon
@ -556,21 +559,21 @@ export const SettingsScreen = withAuthRequired(
/>
</View>
<Text type="lg" style={dangerText}>
Delete my account
<Trans>Delete my account</Trans>
</Text>
</TouchableOpacity>
<View style={styles.spacer20} />
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Developer Tools
<Trans>Developer Tools</Trans>
</Text>
<TouchableOpacity
style={[pal.view, styles.linkCardNoIcon]}
onPress={onPressSystemLog}
accessibilityRole="button"
accessibilityHint="Open system log"
accessibilityLabel="Opens the system log page">
accessibilityLabel={_(msg`Opens the system log page`)}>
<Text type="lg" style={pal.text}>
System log
<Trans>System log</Trans>
</Text>
</TouchableOpacity>
{__DEV__ ? (
@ -588,9 +591,9 @@ export const SettingsScreen = withAuthRequired(
onPress={onPressStorybook}
accessibilityRole="button"
accessibilityHint="Open storybook page"
accessibilityLabel="Opens the storybook page">
accessibilityLabel={_(msg`Opens the storybook page`)}>
<Text type="lg" style={pal.text}>
Storybook
<Trans>Storybook</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -598,9 +601,9 @@ export const SettingsScreen = withAuthRequired(
onPress={onPressResetPreferences}
accessibilityRole="button"
accessibilityHint="Reset preferences"
accessibilityLabel="Resets the preferences state">
accessibilityLabel={_(msg`Resets the preferences state`)}>
<Text type="lg" style={pal.text}>
Reset preferences state
<Trans>Reset preferences state</Trans>
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -608,9 +611,9 @@ export const SettingsScreen = withAuthRequired(
onPress={onPressResetOnboarding}
accessibilityRole="button"
accessibilityHint="Reset onboarding"
accessibilityLabel="Resets the onboarding state">
accessibilityLabel={_(msg`Resets the onboarding state`)}>
<Text type="lg" style={pal.text}>
Reset onboarding state
<Trans>Reset onboarding state</Trans>
</Text>
</TouchableOpacity>
</>
@ -620,7 +623,9 @@ export const SettingsScreen = withAuthRequired(
accessibilityRole="button"
onPress={onPressBuildInfo}>
<Text type="sm" style={[styles.buildInfo, pal.textLight]}>
Build version {AppInfo.appVersion} {AppInfo.updateChannel}
<Trans>
Build version {AppInfo.appVersion} {AppInfo.updateChannel}
</Trans>
</Text>
</TouchableOpacity>
<Text type="sm" style={[pal.textLight]}>
@ -630,7 +635,7 @@ export const SettingsScreen = withAuthRequired(
accessibilityRole="button"
onPress={onPressStatusPage}>
<Text type="sm" style={[styles.buildInfo, pal.textLight]}>
Status page
<Trans>Status page</Trans>
</Text>
</TouchableOpacity>
</View>
@ -646,6 +651,7 @@ const EmailConfirmationNotice = observer(
const pal = usePalette('default')
const palInverted = usePalette('inverted')
const store = useStores()
const {_} = useLingui()
const {isMobile} = useWebMediaQueries()
const {openModal} = useModalControls()
@ -656,7 +662,7 @@ const EmailConfirmationNotice = observer(
return (
<View style={{marginBottom: 20}}>
<Text type="xl-bold" style={[pal.text, styles.heading]}>
Verify email
<Trans>Verify email</Trans>
</Text>
<View
style={[
@ -681,7 +687,7 @@ const EmailConfirmationNotice = observer(
isMobile && {flex: 1},
]}
accessibilityRole="button"
accessibilityLabel="Verify my email"
accessibilityLabel={_(msg`Verify my email`)}
accessibilityHint=""
onPress={() => openModal({name: 'verify-email'})}>
<FontAwesomeIcon
@ -690,12 +696,12 @@ const EmailConfirmationNotice = observer(
size={16}
/>
<Text type="button" style={palInverted.text}>
Verify My Email
<Trans>Verify My Email</Trans>
</Text>
</Pressable>
</View>
<Text style={pal.textLight}>
Protect your account by verifying your email.
<Trans>Protect your account by verifying your email.</Trans>
</Text>
</View>
</View>