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:
parent
82059b7ee1
commit
4c7850f8c4
108 changed files with 10334 additions and 1365 deletions
|
@ -9,10 +9,14 @@ import {useStores} from 'state/index'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {DropdownItem, NativeDropdown} from './forms/NativeDropdown'
|
||||
import * as Toast from '../../com/util/Toast'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
export function AccountDropdownBtn({handle}: {handle: string}) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
|
||||
const items: DropdownItem[] = [
|
||||
{
|
||||
label: 'Remove account',
|
||||
|
@ -34,7 +38,7 @@ export function AccountDropdownBtn({handle}: {handle: string}) {
|
|||
<NativeDropdown
|
||||
testID="accountSettingsDropdownBtn"
|
||||
items={items}
|
||||
accessibilityLabel="Account options"
|
||||
accessibilityLabel={_(msg`Account options`)}
|
||||
accessibilityHint="">
|
||||
<FontAwesomeIcon
|
||||
icon="ellipsis-h"
|
||||
|
|
|
@ -6,6 +6,7 @@ import Animated, {
|
|||
interpolate,
|
||||
useAnimatedStyle,
|
||||
} from 'react-native-reanimated'
|
||||
import {t} from '@lingui/macro'
|
||||
|
||||
export function createCustomBackdrop(
|
||||
onClose?: (() => void) | undefined,
|
||||
|
@ -29,7 +30,7 @@ export function createCustomBackdrop(
|
|||
return (
|
||||
<TouchableWithoutFeedback
|
||||
onPress={onClose}
|
||||
accessibilityLabel="Close bottom drawer"
|
||||
accessibilityLabel={t`Close bottom drawer`}
|
||||
accessibilityHint=""
|
||||
onAccessibilityEscape={() => {
|
||||
if (onClose !== undefined) {
|
||||
|
|
|
@ -16,6 +16,8 @@ import {isWeb, isAndroid} from 'platform/detection'
|
|||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {UserPreviewLink} from './UserPreviewLink'
|
||||
import {DropdownItem, NativeDropdown} from './forms/NativeDropdown'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
export type UserAvatarType = 'user' | 'algo' | 'list'
|
||||
|
||||
|
@ -184,6 +186,7 @@ export function EditableUserAvatar({
|
|||
}: EditableUserAvatarProps) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
||||
const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
|
||||
|
||||
|
@ -294,7 +297,7 @@ export function EditableUserAvatar({
|
|||
<NativeDropdown
|
||||
testID="changeAvatarBtn"
|
||||
items={dropdownItems}
|
||||
accessibilityLabel="Image options"
|
||||
accessibilityLabel={_(msg`Image options`)}
|
||||
accessibilityHint="">
|
||||
{avatar ? (
|
||||
<HighPriorityImage
|
||||
|
|
|
@ -14,6 +14,8 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||
import {isWeb, isAndroid} from 'platform/detection'
|
||||
import {Image as RNImage} from 'react-native-image-crop-picker'
|
||||
import {NativeDropdown, DropdownItem} from './forms/NativeDropdown'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
export function UserBanner({
|
||||
banner,
|
||||
|
@ -26,6 +28,7 @@ export function UserBanner({
|
|||
}) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
||||
const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
|
||||
|
||||
|
@ -112,7 +115,7 @@ export function UserBanner({
|
|||
<NativeDropdown
|
||||
testID="changeBannerBtn"
|
||||
items={dropdownItems}
|
||||
accessibilityLabel="Image options"
|
||||
accessibilityLabel={_(msg`Image options`)}
|
||||
accessibilityHint="">
|
||||
{banner ? (
|
||||
<Image
|
||||
|
|
|
@ -13,6 +13,8 @@ import {
|
|||
import {Text} from '../text/Text'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
export function ErrorMessage({
|
||||
message,
|
||||
|
@ -27,6 +29,7 @@ export function ErrorMessage({
|
|||
}) {
|
||||
const theme = useTheme()
|
||||
const pal = usePalette('error')
|
||||
const {_} = useLingui()
|
||||
return (
|
||||
<View testID="errorMessageView" style={[styles.outer, pal.view, style]}>
|
||||
<View
|
||||
|
@ -49,7 +52,7 @@ export function ErrorMessage({
|
|||
style={styles.btn}
|
||||
onPress={onPressTryAgain}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Retry"
|
||||
accessibilityLabel={_(msg`Retry`)}
|
||||
accessibilityHint="Retries the last action, which errored out">
|
||||
<FontAwesomeIcon
|
||||
icon="arrows-rotate"
|
||||
|
|
|
@ -9,6 +9,8 @@ import {useTheme} from 'lib/ThemeContext'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {Button} from '../forms/Button'
|
||||
import {CenteredView} from '../Views'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export function ErrorScreen({
|
||||
title,
|
||||
|
@ -25,6 +27,8 @@ export function ErrorScreen({
|
|||
}) {
|
||||
const theme = useTheme()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
|
||||
return (
|
||||
<CenteredView testID={testID} style={[styles.outer, pal.view]}>
|
||||
<View style={styles.errorIconContainer}>
|
||||
|
@ -58,7 +62,7 @@ export function ErrorScreen({
|
|||
type="default"
|
||||
style={[styles.btn]}
|
||||
onPress={onPressTryAgain}
|
||||
accessibilityLabel="Retry"
|
||||
accessibilityLabel={_(msg`Retry`)}
|
||||
accessibilityHint="Retries the last action, which errored out">
|
||||
<FontAwesomeIcon
|
||||
icon="arrows-rotate"
|
||||
|
@ -66,7 +70,7 @@ export function ErrorScreen({
|
|||
size={16}
|
||||
/>
|
||||
<Text type="button" style={[styles.btnText, pal.link]}>
|
||||
Try again
|
||||
<Trans>Try again</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
</View>
|
||||
|
|
|
@ -17,6 +17,8 @@ import {colors} from 'lib/styles'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {HITSLOP_10} from 'lib/constants'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
const ESTIMATED_BTN_HEIGHT = 50
|
||||
const ESTIMATED_SEP_HEIGHT = 16
|
||||
|
@ -207,6 +209,7 @@ const DropdownItems = ({
|
|||
}: DropDownItemProps) => {
|
||||
const pal = usePalette('default')
|
||||
const theme = useTheme()
|
||||
const {_} = useLingui()
|
||||
const dropDownBackgroundColor =
|
||||
theme.colorScheme === 'dark' ? pal.btn : pal.view
|
||||
const separatorColor =
|
||||
|
@ -224,7 +227,7 @@ const DropdownItems = ({
|
|||
{/* This TouchableWithoutFeedback renders the background so if the user clicks outside, the dropdown closes */}
|
||||
<TouchableWithoutFeedback
|
||||
onPress={onOuterPress}
|
||||
accessibilityLabel="Toggle dropdown"
|
||||
accessibilityLabel={_(msg`Toggle dropdown`)}
|
||||
accessibilityHint="">
|
||||
<View style={[styles.bg]} />
|
||||
</TouchableWithoutFeedback>
|
||||
|
|
|
@ -9,6 +9,8 @@ import {
|
|||
DropdownItem as NativeDropdownItem,
|
||||
} from './NativeDropdown'
|
||||
import {EventStopper} from '../EventStopper'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function PostDropdownBtn({
|
||||
|
@ -38,6 +40,7 @@ export function PostDropdownBtn({
|
|||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const {_} = useLingui()
|
||||
const defaultCtrlColor = theme.palette.default.postCtrl
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
|
@ -152,7 +155,7 @@ export function PostDropdownBtn({
|
|||
<NativeDropdown
|
||||
testID={testID}
|
||||
items={dropdownItems}
|
||||
accessibilityLabel="More post options"
|
||||
accessibilityLabel={_(msg`More post options`)}
|
||||
accessibilityHint="">
|
||||
<View style={style}>
|
||||
<FontAwesomeIcon icon="ellipsis" size={20} color={defaultCtrlColor} />
|
||||
|
|
|
@ -14,6 +14,8 @@ import {
|
|||
import {MagnifyingGlassIcon} from 'lib/icons'
|
||||
import {useTheme} from 'lib/ThemeContext'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
interface Props {
|
||||
query: string
|
||||
|
@ -33,6 +35,7 @@ export function SearchInput({
|
|||
}: Props) {
|
||||
const theme = useTheme()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const textInput = React.useRef<TextInput>(null)
|
||||
|
||||
const onPressCancelSearchInner = React.useCallback(() => {
|
||||
|
@ -58,7 +61,7 @@ export function SearchInput({
|
|||
onChangeText={onChangeQuery}
|
||||
onSubmitEditing={onSubmitQuery}
|
||||
accessibilityRole="search"
|
||||
accessibilityLabel="Search"
|
||||
accessibilityLabel={_(msg`Search`)}
|
||||
accessibilityHint=""
|
||||
autoCorrect={false}
|
||||
autoCapitalize="none"
|
||||
|
@ -67,7 +70,7 @@ export function SearchInput({
|
|||
<TouchableOpacity
|
||||
onPress={onPressCancelSearchInner}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Clear search query"
|
||||
accessibilityLabel={_(msg`Clear search query`)}
|
||||
accessibilityHint="">
|
||||
<FontAwesomeIcon
|
||||
icon="xmark"
|
||||
|
|
|
@ -6,6 +6,8 @@ import {ModerationUI} from '@atproto/api'
|
|||
import {Text} from '../text/Text'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function ContentHider({
|
||||
|
@ -23,6 +25,7 @@ export function ContentHider({
|
|||
childContainerStyle?: StyleProp<ViewStyle>
|
||||
}>) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [override, setOverride] = React.useState(false)
|
||||
const {openModal} = useModalControls()
|
||||
|
@ -69,7 +72,7 @@ export function ContentHider({
|
|||
})
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Learn more about this warning"
|
||||
accessibilityLabel={_(msg`Learn more about this warning`)}
|
||||
accessibilityHint="">
|
||||
<ShieldExclamation size={18} style={pal.text} />
|
||||
</Pressable>
|
||||
|
|
|
@ -5,6 +5,8 @@ import {Text} from '../text/Text'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function PostAlerts({
|
||||
|
@ -16,6 +18,7 @@ export function PostAlerts({
|
|||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const shouldAlert = !!moderation.cause && moderation.alert
|
||||
|
@ -34,14 +37,14 @@ export function PostAlerts({
|
|||
})
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Learn more about this warning"
|
||||
accessibilityLabel={_(msg`Learn more about this warning`)}
|
||||
accessibilityHint=""
|
||||
style={[styles.container, pal.viewLight, style]}>
|
||||
<ShieldExclamation style={pal.text} size={16} />
|
||||
<Text type="lg" style={[pal.text]}>
|
||||
{desc.name}{' '}
|
||||
<Text type="lg" style={[pal.link, styles.learnMoreBtn]}>
|
||||
Learn More
|
||||
<Trans>Learn More</Trans>
|
||||
</Text>
|
||||
</Text>
|
||||
</Pressable>
|
||||
|
|
|
@ -8,6 +8,8 @@ import {Text} from '../text/Text'
|
|||
import {addStyle} from 'lib/styles'
|
||||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
interface Props extends ComponentProps<typeof Link> {
|
||||
|
@ -26,6 +28,7 @@ export function PostHider({
|
|||
...props
|
||||
}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const [override, setOverride] = React.useState(false)
|
||||
const {openModal} = useModalControls()
|
||||
|
@ -70,7 +73,7 @@ export function PostHider({
|
|||
})
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Learn more about this warning"
|
||||
accessibilityLabel={_(msg`Learn more about this warning`)}
|
||||
accessibilityHint="">
|
||||
<ShieldExclamation size={18} style={pal.text} />
|
||||
</Pressable>
|
||||
|
|
|
@ -8,6 +8,8 @@ import {
|
|||
describeModerationCause,
|
||||
getProfileModerationCauses,
|
||||
} from 'lib/moderation'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function ProfileHeaderAlerts({
|
||||
|
@ -18,6 +20,7 @@ export function ProfileHeaderAlerts({
|
|||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
const causes = getProfileModerationCauses(moderation)
|
||||
|
@ -41,7 +44,7 @@ export function ProfileHeaderAlerts({
|
|||
})
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Learn more about this warning"
|
||||
accessibilityLabel={_(msg`Learn more about this warning`)}
|
||||
accessibilityHint=""
|
||||
style={[styles.container, pal.viewLight, style]}>
|
||||
<ShieldExclamation style={pal.text} size={24} />
|
||||
|
@ -49,7 +52,7 @@ export function ProfileHeaderAlerts({
|
|||
{desc.name}
|
||||
</Text>
|
||||
<Text type="lg" style={[pal.link, styles.learnMoreBtn]}>
|
||||
Learn More
|
||||
<Trans>Learn More</Trans>
|
||||
</Text>
|
||||
</Pressable>
|
||||
)
|
||||
|
|
|
@ -18,7 +18,10 @@ import {NavigationProp} from 'lib/routes/types'
|
|||
import {Text} from '../text/Text'
|
||||
import {Button} from '../forms/Button'
|
||||
import {describeModerationCause} from 'lib/moderation'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {s} from '#/lib/styles'
|
||||
|
||||
export function ScreenHider({
|
||||
testID,
|
||||
|
@ -36,6 +39,7 @@ export function ScreenHider({
|
|||
}>) {
|
||||
const pal = usePalette('default')
|
||||
const palInverted = usePalette('inverted')
|
||||
const {_} = useLingui()
|
||||
const [override, setOverride] = React.useState(false)
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
|
@ -62,14 +66,13 @@ export function ScreenHider({
|
|||
</View>
|
||||
</View>
|
||||
<Text type="title-2xl" style={[styles.title, pal.text]}>
|
||||
Content Warning
|
||||
<Trans>Content Warning</Trans>
|
||||
</Text>
|
||||
<Text type="2xl" style={[styles.description, pal.textLight]}>
|
||||
This {screenDescription} has been flagged:{' '}
|
||||
<Text type="2xl-medium" style={pal.text}>
|
||||
{desc.name}
|
||||
<Trans>This {screenDescription} has been flagged:</Trans>
|
||||
<Text type="2xl-medium" style={[pal.text, s.ml5]}>
|
||||
{desc.name}.
|
||||
</Text>
|
||||
.{' '}
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() => {
|
||||
openModal({
|
||||
|
@ -79,10 +82,10 @@ export function ScreenHider({
|
|||
})
|
||||
}}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Learn more about this warning"
|
||||
accessibilityLabel={_(msg`Learn more about this warning`)}
|
||||
accessibilityHint="">
|
||||
<Text type="2xl" style={pal.link}>
|
||||
Learn More
|
||||
<Trans>Learn More</Trans>
|
||||
</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</Text>
|
||||
|
@ -99,7 +102,7 @@ export function ScreenHider({
|
|||
}}
|
||||
style={styles.btn}>
|
||||
<Text type="button-lg" style={pal.textInverted}>
|
||||
Go back
|
||||
<Trans>Go back</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
{!moderation.noOverride && (
|
||||
|
@ -108,7 +111,7 @@ export function ScreenHider({
|
|||
onPress={() => setOverride(v => !v)}
|
||||
style={styles.btn}>
|
||||
<Text type="button-lg" style={pal.text}>
|
||||
Show anyway
|
||||
<Trans>Show anyway</Trans>
|
||||
</Text>
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -10,6 +10,8 @@ import {
|
|||
DropdownItem as NativeDropdownItem,
|
||||
} from '../forms/NativeDropdown'
|
||||
import {EventStopper} from '../EventStopper'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
interface Props {
|
||||
isReposted: boolean
|
||||
|
@ -28,6 +30,7 @@ export const RepostButton = ({
|
|||
onQuote,
|
||||
}: Props) => {
|
||||
const theme = useTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
const defaultControlColor = React.useMemo(
|
||||
() => ({
|
||||
|
@ -63,7 +66,7 @@ export const RepostButton = ({
|
|||
<EventStopper>
|
||||
<NativeDropdown
|
||||
items={dropdownItems}
|
||||
accessibilityLabel="Repost or quote post"
|
||||
accessibilityLabel={_(msg`Repost or quote post`)}
|
||||
accessibilityHint="">
|
||||
<View
|
||||
style={[
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue