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
|
@ -49,6 +49,8 @@ import {LabelsBtn} from './labels/LabelsBtn'
|
|||
import {SelectLangBtn} from './select-language/SelectLangBtn'
|
||||
import {EmojiPickerButton} from './text-input/web/EmojiPicker.web'
|
||||
import {insertMentionAt} from 'lib/strings/mention-manip'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModals, useModalControls} from '#/state/modals'
|
||||
import {useRequireAltTextEnabled} from '#/state/preferences'
|
||||
import {
|
||||
|
@ -70,6 +72,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
const pal = usePalette('default')
|
||||
const {isDesktop, isMobile} = useWebMediaQueries()
|
||||
const store = useStores()
|
||||
const {_} = useLingui()
|
||||
const requireAltTextEnabled = useRequireAltTextEnabled()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useLanguagePrefsApi()
|
||||
|
@ -273,9 +276,11 @@ export const ComposePost = observer(function ComposePost({
|
|||
onPress={onPressCancel}
|
||||
onAccessibilityEscape={onPressCancel}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Cancel"
|
||||
accessibilityLabel={_(msg`Cancel`)}
|
||||
accessibilityHint="Closes post composer and discards post draft">
|
||||
<Text style={[pal.link, s.f18]}>Cancel</Text>
|
||||
<Text style={[pal.link, s.f18]}>
|
||||
<Trans>Cancel</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={s.flex1} />
|
||||
{isProcessing ? (
|
||||
|
@ -316,7 +321,9 @@ export const ComposePost = observer(function ComposePost({
|
|||
</TouchableOpacity>
|
||||
) : (
|
||||
<View style={[styles.postBtn, pal.btn]}>
|
||||
<Text style={[pal.textLight, s.f16, s.bold]}>Post</Text>
|
||||
<Text style={[pal.textLight, s.f16, s.bold]}>
|
||||
<Trans>Post</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
|
@ -332,7 +339,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
/>
|
||||
</View>
|
||||
<Text style={[pal.text, s.flex1]}>
|
||||
One or more images is missing alt text.
|
||||
<Trans>One or more images is missing alt text.</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
@ -388,7 +395,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
onSuggestedLinksChanged={setSuggestedLinks}
|
||||
onError={setError}
|
||||
accessible={true}
|
||||
accessibilityLabel="Write post"
|
||||
accessibilityLabel={_(msg`Write post`)}
|
||||
accessibilityHint={`Compose posts up to ${MAX_GRAPHEME_LENGTH} characters in length`}
|
||||
/>
|
||||
</View>
|
||||
|
@ -417,11 +424,11 @@ export const ComposePost = observer(function ComposePost({
|
|||
style={[pal.borderDark, styles.addExtLinkBtn]}
|
||||
onPress={() => onPressAddLinkCard(url)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Add link card"
|
||||
accessibilityLabel={_(msg`Add link card`)}
|
||||
accessibilityHint={`Creates a card with a thumbnail. The card links to ${url}`}>
|
||||
<Text style={pal.text}>
|
||||
Add link card:{' '}
|
||||
<Text style={pal.link}>{toShortUrl(url)}</Text>
|
||||
<Trans>Add link card:</Trans>
|
||||
<Text style={[pal.link, s.ml5]}>{toShortUrl(url)}</Text>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
|
|
|
@ -11,6 +11,8 @@ import {Text} from '../util/text/Text'
|
|||
import {s} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {ExternalEmbedDraft} from 'lib/api/index'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
export const ExternalEmbed = ({
|
||||
link,
|
||||
|
@ -21,6 +23,7 @@ export const ExternalEmbed = ({
|
|||
}) => {
|
||||
const pal = usePalette('default')
|
||||
const palError = usePalette('error')
|
||||
const {_} = useLingui()
|
||||
if (!link) {
|
||||
return <View />
|
||||
}
|
||||
|
@ -64,7 +67,7 @@ export const ExternalEmbed = ({
|
|||
style={styles.removeBtn}
|
||||
onPress={onRemove}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Remove image preview"
|
||||
accessibilityLabel={_(msg`Remove image preview`)}
|
||||
accessibilityHint={`Removes default thumbnail from ${link.uri}`}
|
||||
onAccessibilityEscape={onRemove}>
|
||||
<FontAwesomeIcon size={18} icon="xmark" style={s.white} />
|
||||
|
|
|
@ -5,10 +5,13 @@ import {Text} from '../util/text/Text'
|
|||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useStores} from 'state/index'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) {
|
||||
const store = useStores()
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isDesktop} = useWebMediaQueries()
|
||||
return (
|
||||
<TouchableOpacity
|
||||
|
@ -16,7 +19,7 @@ export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) {
|
|||
style={[pal.view, pal.border, styles.prompt]}
|
||||
onPress={() => onPressCompose()}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Compose reply"
|
||||
accessibilityLabel={_(msg`Compose reply`)}
|
||||
accessibilityHint="Opens composer">
|
||||
<UserAvatar avatar={store.me.avatar} size={38} />
|
||||
<Text
|
||||
|
@ -25,7 +28,7 @@ export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) {
|
|||
pal.text,
|
||||
isDesktop ? styles.labelDesktopWeb : styles.labelMobile,
|
||||
]}>
|
||||
Write your reply
|
||||
<Trans>Write your reply</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
|
|
|
@ -7,6 +7,8 @@ import {ShieldExclamation} from 'lib/icons'
|
|||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
||||
import {isNative} from 'platform/detection'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export const LabelsBtn = observer(function LabelsBtn({
|
||||
|
@ -19,6 +21,7 @@ export const LabelsBtn = observer(function LabelsBtn({
|
|||
onChange: (v: string[]) => void
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
return (
|
||||
|
@ -26,7 +29,7 @@ export const LabelsBtn = observer(function LabelsBtn({
|
|||
type="default-light"
|
||||
testID="labelsBtn"
|
||||
style={[styles.button, !hasMedia && styles.dimmed]}
|
||||
accessibilityLabel="Content warnings"
|
||||
accessibilityLabel={_(msg`Content warnings`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
if (isNative) {
|
||||
|
|
|
@ -10,6 +10,8 @@ import {Text} from 'view/com/util/text/Text'
|
|||
import {Dimensions} from 'lib/media/types'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {isNative} from 'platform/detection'
|
||||
|
||||
|
@ -48,6 +50,7 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
containerInfo,
|
||||
}: GalleryInnerProps) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
|
@ -113,7 +116,7 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
<TouchableOpacity
|
||||
testID="altTextButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Add alt text"
|
||||
accessibilityLabel={_(msg`Add alt text`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
Keyboard.dismiss()
|
||||
|
@ -124,7 +127,7 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
}}
|
||||
style={[styles.altTextControl, altTextControlStyle]}>
|
||||
<Text style={styles.altTextControlLabel} accessible={false}>
|
||||
ALT
|
||||
<Trans>ALT</Trans>
|
||||
</Text>
|
||||
{image.altText.length > 0 ? (
|
||||
<FontAwesomeIcon
|
||||
|
@ -138,7 +141,7 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
<TouchableOpacity
|
||||
testID="editPhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Edit image"
|
||||
accessibilityLabel={_(msg`Edit image`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
if (isNative) {
|
||||
|
@ -161,7 +164,7 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
<TouchableOpacity
|
||||
testID="removePhotoButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Remove image"
|
||||
accessibilityLabel={_(msg`Remove image`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => gallery.remove(image)}
|
||||
style={styles.imageControl}>
|
||||
|
@ -174,7 +177,7 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
</View>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Add alt text"
|
||||
accessibilityLabel={_(msg`Add alt text`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => {
|
||||
Keyboard.dismiss()
|
||||
|
@ -203,8 +206,10 @@ const GalleryInner = observer(function GalleryImpl({
|
|||
<FontAwesomeIcon icon="info" size={12} color={pal.colors.text} />
|
||||
</View>
|
||||
<Text type="sm" style={[pal.textLight, s.flex1]}>
|
||||
Alt text describes images for blind and low-vision users, and helps
|
||||
give context to everyone.
|
||||
<Trans>
|
||||
Alt text describes images for blind and low-vision users, and helps
|
||||
give context to everyone.
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</>
|
||||
|
|
|
@ -13,6 +13,8 @@ import {HITSLOP_10, POST_IMG_MAX} from 'lib/constants'
|
|||
import {GalleryModel} from 'state/models/media/gallery'
|
||||
import {isMobileWeb, isNative} from 'platform/detection'
|
||||
import {logger} from '#/logger'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
type Props = {
|
||||
gallery: GalleryModel
|
||||
|
@ -22,6 +24,7 @@ export function OpenCameraBtn({gallery}: Props) {
|
|||
const pal = usePalette('default')
|
||||
const {track} = useAnalytics()
|
||||
const store = useStores()
|
||||
const {_} = useLingui()
|
||||
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
||||
|
||||
const onPressTakePicture = useCallback(async () => {
|
||||
|
@ -56,7 +59,7 @@ export function OpenCameraBtn({gallery}: Props) {
|
|||
style={styles.button}
|
||||
hitSlop={HITSLOP_10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Camera"
|
||||
accessibilityLabel={_(msg`Camera`)}
|
||||
accessibilityHint="Opens camera on device">
|
||||
<FontAwesomeIcon
|
||||
icon="camera"
|
||||
|
|
|
@ -10,6 +10,8 @@ import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions'
|
|||
import {GalleryModel} from 'state/models/media/gallery'
|
||||
import {HITSLOP_10} from 'lib/constants'
|
||||
import {isNative} from 'platform/detection'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
|
||||
type Props = {
|
||||
gallery: GalleryModel
|
||||
|
@ -18,6 +20,7 @@ type Props = {
|
|||
export function SelectPhotoBtn({gallery}: Props) {
|
||||
const pal = usePalette('default')
|
||||
const {track} = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
|
||||
|
||||
const onPressSelectPhotos = useCallback(async () => {
|
||||
|
@ -37,7 +40,7 @@ export function SelectPhotoBtn({gallery}: Props) {
|
|||
style={styles.button}
|
||||
hitSlop={HITSLOP_10}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Gallery"
|
||||
accessibilityLabel={_(msg`Gallery`)}
|
||||
accessibilityHint="Opens device photo gallery">
|
||||
<FontAwesomeIcon
|
||||
icon={['far', 'image']}
|
||||
|
|
|
@ -21,9 +21,12 @@ import {
|
|||
toPostLanguages,
|
||||
hasPostLanguage,
|
||||
} from '#/state/preferences/languages'
|
||||
import {t, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
export const SelectLangBtn = observer(function SelectLangBtn() {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {openModal} = useModalControls()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useLanguagePrefsApi()
|
||||
|
@ -82,11 +85,11 @@ export const SelectLangBtn = observer(function SelectLangBtn() {
|
|||
}
|
||||
|
||||
return [
|
||||
{heading: true, label: 'Post language'},
|
||||
{heading: true, label: t`Post language`},
|
||||
...arr.slice(0, 6),
|
||||
{sep: true},
|
||||
{
|
||||
label: 'Other...',
|
||||
label: t`Other...`,
|
||||
onPress: onPressMore,
|
||||
},
|
||||
]
|
||||
|
@ -99,7 +102,7 @@ export const SelectLangBtn = observer(function SelectLangBtn() {
|
|||
items={items}
|
||||
openUpwards
|
||||
style={styles.button}
|
||||
accessibilityLabel="Language selection"
|
||||
accessibilityLabel={_(msg`Language selection`)}
|
||||
accessibilityHint="">
|
||||
{postLanguagesPref.length > 0 ? (
|
||||
<Text type="lg-bold" style={[pal.link, styles.label]} numberOfLines={1}>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue