Add plural formatting, translate and tweak some accessibility strings in composer (#7997)

* add plural formatting and translate accessibility strings

* tweak accessibility label and add comments for translators

* fix

* tweak comments

* make label conditionally plural

* rm stray bracket

* try to fix prettier

* nope

* lint

* more lint

* prettier

---------

Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
surfdude29
2025-04-02 23:44:03 +01:00
committed by GitHub
parent 0897a692da
commit 4a7fa07e87
2 changed files with 71 additions and 26 deletions
+69 -24
View File
@@ -12,17 +12,17 @@ import {
BackHandler, BackHandler,
Keyboard, Keyboard,
KeyboardAvoidingView, KeyboardAvoidingView,
LayoutChangeEvent, type LayoutChangeEvent,
ScrollView, ScrollView,
StyleProp, type StyleProp,
StyleSheet, StyleSheet,
View, View,
ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
// @ts-expect-error no type definition // @ts-expect-error no type definition
import ProgressCircle from 'react-native-progress/Circle' import ProgressCircle from 'react-native-progress/Circle'
import Animated, { import Animated, {
AnimatedRef, type AnimatedRef,
Easing, Easing,
FadeIn, FadeIn,
FadeOut, FadeOut,
@@ -41,15 +41,15 @@ import Animated, {
ZoomOut, ZoomOut,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {ImagePickerAsset} from 'expo-image-picker' import {type ImagePickerAsset} from 'expo-image-picker'
import { import {
AppBskyFeedDefs, AppBskyFeedDefs,
AppBskyFeedGetPostThread, type AppBskyFeedGetPostThread,
BskyAgent, type BskyAgent,
RichText, type RichText,
} from '@atproto/api' } from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro' import {msg, plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
@@ -59,7 +59,7 @@ import {until} from '#/lib/async/until'
import { import {
MAX_GRAPHEME_LENGTH, MAX_GRAPHEME_LENGTH,
SUPPORTED_MIME_TYPES, SUPPORTED_MIME_TYPES,
SupportedMimeTypes, type SupportedMimeTypes,
} from '#/lib/constants' } from '#/lib/constants'
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED' import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
import {useEmail} from '#/lib/hooks/useEmail' import {useEmail} from '#/lib/hooks/useEmail'
@@ -75,7 +75,7 @@ import {logger} from '#/logger'
import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection' import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection'
import {useDialogStateControlContext} from '#/state/dialogs' import {useDialogStateControlContext} from '#/state/dialogs'
import {emitPostCreated} from '#/state/events' import {emitPostCreated} from '#/state/events'
import {ComposerImage, pasteImage} from '#/state/gallery' import {type ComposerImage, pasteImage} from '#/state/gallery'
import {useModalControls} from '#/state/modals' import {useModalControls} from '#/state/modals'
import {useRequireAltTextEnabled} from '#/state/preferences' import {useRequireAltTextEnabled} from '#/state/preferences'
import { import {
@@ -85,10 +85,10 @@ import {
} from '#/state/preferences/languages' } from '#/state/preferences/languages'
import {usePreferencesQuery} from '#/state/queries/preferences' import {usePreferencesQuery} from '#/state/queries/preferences'
import {useProfileQuery} from '#/state/queries/profile' import {useProfileQuery} from '#/state/queries/profile'
import {Gif} from '#/state/queries/tenor' import {type Gif} from '#/state/queries/tenor'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
import {useComposerControls} from '#/state/shell/composer' import {useComposerControls} from '#/state/shell/composer'
import {ComposerOpts} from '#/state/shell/composer' import {type ComposerOpts} from '#/state/shell/composer'
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress' import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
import {ComposerReplyTo} from '#/view/com/composer/ComposerReplyTo' import {ComposerReplyTo} from '#/view/com/composer/ComposerReplyTo'
import { import {
@@ -105,7 +105,10 @@ import {SelectLangBtn} from '#/view/com/composer/select-language/SelectLangBtn'
import {SuggestedLanguage} from '#/view/com/composer/select-language/SuggestedLanguage' import {SuggestedLanguage} from '#/view/com/composer/select-language/SuggestedLanguage'
// TODO: Prevent naming components that coincide with RN primitives // TODO: Prevent naming components that coincide with RN primitives
// due to linting false positives // due to linting false positives
import {TextInput, TextInputRef} from '#/view/com/composer/text-input/TextInput' import {
TextInput,
type TextInputRef,
} from '#/view/com/composer/text-input/TextInput'
import {ThreadgateBtn} from '#/view/com/composer/threadgate/ThreadgateBtn' import {ThreadgateBtn} from '#/view/com/composer/threadgate/ThreadgateBtn'
import {SelectVideoBtn} from '#/view/com/composer/videos/SelectVideoBtn' import {SelectVideoBtn} from '#/view/com/composer/videos/SelectVideoBtn'
import {SubtitleDialogBtn} from '#/view/com/composer/videos/SubtitleDialog' import {SubtitleDialogBtn} from '#/view/com/composer/videos/SubtitleDialog'
@@ -126,16 +129,21 @@ import * as Prompt from '#/components/Prompt'
import {Text as NewText} from '#/components/Typography' import {Text as NewText} from '#/components/Typography'
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet' import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
import { import {
ComposerAction, type ComposerAction,
composerReducer, composerReducer,
createComposerState, createComposerState,
EmbedDraft, type EmbedDraft,
MAX_IMAGES, MAX_IMAGES,
PostAction, type PostAction,
PostDraft, type PostDraft,
ThreadDraft, type ThreadDraft,
} from './state/composer' } from './state/composer'
import {NO_VIDEO, NoVideoState, processVideo, VideoState} from './state/video' import {
NO_VIDEO,
type NoVideoState,
processVideo,
type VideoState,
} from './state/video'
import {getVideoMetadata} from './videos/pickVideo' import {getVideoMetadata} from './videos/pickVideo'
import {clearThumbnailCache} from './videos/VideoTranscodeBackdrop' import {clearThumbnailCache} from './videos/VideoTranscodeBackdrop'
@@ -835,7 +843,9 @@ let ComposerPost = React.memo(function ComposerPost({
accessible={true} accessible={true}
accessibilityLabel={_(msg`Write post`)} accessibilityLabel={_(msg`Write post`)}
accessibilityHint={_( accessibilityHint={_(
msg`Compose posts up to ${MAX_GRAPHEME_LENGTH} characters in length`, msg`Compose posts up to ${plural(MAX_GRAPHEME_LENGTH || 0, {
other: '# characters',
})} in length`,
)} )}
/> />
</View> </View>
@@ -917,20 +927,23 @@ function ComposerTopBar({
children?: React.ReactNode children?: React.ReactNode
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui()
return ( return (
<Animated.View <Animated.View
style={topBarAnimatedStyle} style={topBarAnimatedStyle}
layout={native(LinearTransition)}> layout={native(LinearTransition)}>
<View style={styles.topbarInner}> <View style={styles.topbarInner}>
<Button <Button
label="Cancel" label={_(msg`Cancel`)}
variant="ghost" variant="ghost"
color="primary" color="primary"
shape="default" shape="default"
size="small" size="small"
style={[a.rounded_full, a.py_sm, {paddingLeft: 7, paddingRight: 7}]} style={[a.rounded_full, a.py_sm, {paddingLeft: 7, paddingRight: 7}]}
onPress={onCancel} onPress={onCancel}
accessibilityHint="Closes post composer and discards post draft"> accessibilityHint={_(
msg`Closes post composer and discards post draft`,
)}>
<ButtonText style={[a.text_md]}> <ButtonText style={[a.text_md]}>
<Trans>Cancel</Trans> <Trans>Cancel</Trans>
</ButtonText> </ButtonText>
@@ -946,7 +959,39 @@ function ComposerTopBar({
) : ( ) : (
<Button <Button
testID="composerPublishBtn" testID="composerPublishBtn"
label={isReply ? 'Publish reply' : 'Publish post'} label={
isReply
? isThread
? _(
msg({
message: 'Publish replies',
comment:
'Accessibility label for button to publish multiple replies in a thread',
}),
)
: _(
msg({
message: 'Publish reply',
comment:
'Accessibility label for button to publish a single reply',
}),
)
: isThread
? _(
msg({
message: 'Publish posts',
comment:
'Accessibility label for button to publish multiple posts in a thread',
}),
)
: _(
msg({
message: 'Publish post',
comment:
'Accessibility label for button to publish a single post',
}),
)
}
variant="solid" variant="solid"
color="primary" color="primary"
shape="default" shape="default"
+2 -2
View File
@@ -113,8 +113,8 @@ function DialogInner({
</Text> </Text>
<Text style={[t.atoms.text_contrast_medium, a.leading_snug]}> <Text style={[t.atoms.text_contrast_medium, a.leading_snug]}>
<Trans> <Trans>
Please add any content warning labels that are applicable for Please add any content warning labels that are applicable for the
the media you are posting. media you are posting.
</Trans> </Trans>
</Text> </Text>
</View> </View>