diff options
author | Ansh <anshnanda10@gmail.com> | 2023-11-20 13:29:27 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-20 13:29:27 -0800 |
commit | c5b6f88e9a694d79126af4f742e66833dfd528bd (patch) | |
tree | 0bfdc49ace558adc3d9d5a76fc4726f16f853d4e /src/view | |
parent | 019aae5f01cb7b503d242917ae0092c2818f3b71 (diff) | |
download | voidsky-c5b6f88e9a694d79126af4f742e66833dfd528bd.tar.zst |
Hindi Internationalization (#1914)
* get basic hindi support to work * get web app language switcher in * Refactor i18n implementation and remove unused code * add missing strings * add dropdowns and modals missing strings * complete all hindi translations * fix merge conflicts * fix legeacy persisted state * fix data in RecommendedFeeds * fix lint
Diffstat (limited to 'src/view')
49 files changed, 517 insertions, 280 deletions
diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.tsx index 941671bf8..d3318bffd 100644 --- a/src/view/com/auth/onboarding/RecommendedFeeds.tsx +++ b/src/view/com/auth/onboarding/RecommendedFeeds.tsx @@ -10,6 +10,8 @@ import {RecommendedFeedsItem} from './RecommendedFeedsItem' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {usePalette} from 'lib/hooks/usePalette' import {ErrorMessage} from 'view/com/util/error/ErrorMessage' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useSuggestedFeedsQuery} from '#/state/queries/suggested-feeds' type Props = { @@ -17,40 +19,45 @@ type Props = { } export function RecommendedFeeds({next}: Props) { const pal = usePalette('default') + const {_} = useLingui() const {isTabletOrMobile} = useWebMediaQueries() const {isLoading, data} = useSuggestedFeedsQuery() - const hasFeeds = data && data?.pages?.[0]?.feeds?.length + const hasFeeds = data && data.pages[0].feeds.length const title = ( <> - <Text - style={[ - pal.textLight, - tdStyles.title1, - isTabletOrMobile && tdStyles.title1Small, - ]}> - Choose your - </Text> - <Text - style={[ - pal.link, - tdStyles.title2, - isTabletOrMobile && tdStyles.title2Small, - ]}> - Recommended - </Text> - <Text - style={[ - pal.link, - tdStyles.title2, - isTabletOrMobile && tdStyles.title2Small, - ]}> - Feeds - </Text> + <Trans> + <Text + style={[ + pal.textLight, + tdStyles.title1, + isTabletOrMobile && tdStyles.title1Small, + ]}> + Choose your + </Text> + <Text + style={[ + pal.link, + tdStyles.title2, + isTabletOrMobile && tdStyles.title2Small, + ]}> + Recommended + </Text> + <Text + style={[ + pal.link, + tdStyles.title2, + isTabletOrMobile && tdStyles.title2Small, + ]}> + Feeds + </Text> + </Trans> <Text type="2xl-medium" style={[pal.textLight, tdStyles.description]}> - Feeds are created by users to curate content. Choose some feeds that you - find interesting. + <Trans> + Feeds are created by users to curate content. Choose some feeds that + you find interesting. + </Trans> </Text> <View style={{ @@ -69,7 +76,7 @@ export function RecommendedFeeds({next}: Props) { <Text type="2xl-medium" style={{color: '#fff', position: 'relative', top: -1}}> - Next + <Trans>Next</Trans> </Text> <FontAwesomeIcon icon="angle-right" color="#fff" size={14} /> </View> @@ -99,20 +106,22 @@ export function RecommendedFeeds({next}: Props) { <ActivityIndicator size="large" /> </View> ) : ( - <ErrorMessage message="Failed to load recommended feeds" /> + <ErrorMessage message={_(msg`Failed to load recommended feeds`)} /> )} </TitleColumnLayout> </TabletOrDesktop> <Mobile> <View style={[mStyles.container]} testID="recommendedFeedsOnboarding"> <ViewHeader - title="Recommended Feeds" + title={_(msg`Recommended Feeds`)} showBackButton={false} showOnDesktop /> <Text type="lg-medium" style={[pal.text, mStyles.header]}> - Check out some recommended feeds. Tap + to add them to your list of - pinned feeds. + <Trans> + Check out some recommended feeds. Tap + to add them to your list + of pinned feeds. + </Trans> </Text> {hasFeeds ? ( @@ -128,13 +137,15 @@ export function RecommendedFeeds({next}: Props) { </View> ) : ( <View style={{flex: 1}}> - <ErrorMessage message="Failed to load recommended feeds" /> + <ErrorMessage + message={_(msg`Failed to load recommended feeds`)} + /> </View> )} <Button onPress={next} - label="Continue" + label={_(msg`Continue`)} testID="continueBtn" style={mStyles.button} labelStyle={mStyles.buttonText} diff --git a/src/view/com/auth/onboarding/RecommendedFollows.tsx b/src/view/com/auth/onboarding/RecommendedFollows.tsx index c327a7168..7bf8c97e4 100644 --- a/src/view/com/auth/onboarding/RecommendedFollows.tsx +++ b/src/view/com/auth/onboarding/RecommendedFollows.tsx @@ -14,12 +14,15 @@ import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows' import {useGetSuggestedFollowersByActor} from '#/state/queries/suggested-follows' import {useModerationOpts} from '#/state/queries/preferences' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = { next: () => void } export function RecommendedFollows({next}: Props) { const pal = usePalette('default') + const {_} = useLingui() const {isTabletOrMobile} = useWebMediaQueries() const {data: suggestedFollows, dataUpdatedAt} = useSuggestedFollowsQuery() const getSuggestedFollowsByActor = useGetSuggestedFollowersByActor() @@ -31,33 +34,37 @@ export function RecommendedFollows({next}: Props) { const title = ( <> - <Text - style={[ - pal.textLight, - tdStyles.title1, - isTabletOrMobile && tdStyles.title1Small, - ]}> - Follow some - </Text> - <Text - style={[ - pal.link, - tdStyles.title2, - isTabletOrMobile && tdStyles.title2Small, - ]}> - Recommended - </Text> - <Text - style={[ - pal.link, - tdStyles.title2, - isTabletOrMobile && tdStyles.title2Small, - ]}> - Users - </Text> + <Trans> + <Text + style={[ + pal.textLight, + tdStyles.title1, + isTabletOrMobile && tdStyles.title1Small, + ]}> + Follow some + </Text> + <Text + style={[ + pal.link, + tdStyles.title2, + isTabletOrMobile && tdStyles.title2Small, + ]}> + Recommended + </Text> + <Text + style={[ + pal.link, + tdStyles.title2, + isTabletOrMobile && tdStyles.title2Small, + ]}> + Users + </Text> + </Trans> <Text type="2xl-medium" style={[pal.textLight, tdStyles.description]}> - Follow some users to get started. We can recommend you more users based - on who you find interesting. + <Trans> + Follow some users to get started. We can recommend you more users + based on who you find interesting. + </Trans> </Text> <View style={{ @@ -76,7 +83,7 @@ export function RecommendedFollows({next}: Props) { <Text type="2xl-medium" style={{color: '#fff', position: 'relative', top: -1}}> - Done + <Trans>Done</Trans> </Text> <FontAwesomeIcon icon="angle-right" color="#fff" size={14} /> </View> @@ -171,13 +178,15 @@ export function RecommendedFollows({next}: Props) { <View style={[mStyles.container]} testID="recommendedFollowsOnboarding"> <View> <ViewHeader - title="Recommended Follows" + title={_(msg`Recommended Users`)} showBackButton={false} showOnDesktop /> <Text type="lg-medium" style={[pal.text, mStyles.header]}> - Check out some recommended users. Follow them to see similar - users. + <Trans> + Check out some recommended users. Follow them to see similar + users. + </Trans> </Text> </View> {!suggestedFollows || !moderationOpts ? ( @@ -199,7 +208,7 @@ export function RecommendedFollows({next}: Props) { )} <Button onPress={next} - label="Continue" + label={_(msg`Continue`)} testID="continueBtn" style={mStyles.button} labelStyle={mStyles.buttonText} diff --git a/src/view/com/auth/onboarding/WelcomeMobile.tsx b/src/view/com/auth/onboarding/WelcomeMobile.tsx index 2717bff3f..5de1a7817 100644 --- a/src/view/com/auth/onboarding/WelcomeMobile.tsx +++ b/src/view/com/auth/onboarding/WelcomeMobile.tsx @@ -43,10 +43,10 @@ export function WelcomeMobile({next, skip}: Props) { /> <View> <Text style={[pal.text, styles.title]}> - Welcome to{' '} - <Text style={[pal.text, pal.link, styles.title]}> - <Trans>Bluesky</Trans> - </Text> + <Trans> + Welcome to{' '} + <Text style={[pal.text, pal.link, styles.title]}>Bluesky</Text> + </Trans> </Text> <View style={styles.spacer} /> <View style={[styles.row]}> diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 50728cba8..6f058d39e 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -129,19 +129,19 @@ export const ComposePost = observer(function ComposePost({ } openModal({ name: 'confirm', - title: 'Discard draft', + title: _(msg`Discard draft`), onPressConfirm: onClose, onPressCancel: () => { closeModal() }, - message: "Are you sure you'd like to discard this draft?", - confirmBtnText: 'Discard', + message: _(msg`Are you sure you'd like to discard this draft?`), + confirmBtnText: _(msg`Discard`), confirmBtnStyle: {backgroundColor: colors.red4}, }) } else { onClose() } - }, [openModal, closeModal, activeModals, onClose, graphemeLength, gallery]) + }, [openModal, closeModal, activeModals, onClose, graphemeLength, gallery, _]) // android back button useEffect(() => { if (!isAndroid) { diff --git a/src/view/com/feeds/FeedSourceCard.tsx b/src/view/com/feeds/FeedSourceCard.tsx index 4e461e4f6..d8b67767b 100644 --- a/src/view/com/feeds/FeedSourceCard.tsx +++ b/src/view/com/feeds/FeedSourceCard.tsx @@ -14,6 +14,8 @@ import * as Toast from 'view/com/util/Toast' import {sanitizeHandle} from 'lib/strings/handles' import {logger} from '#/logger' import {useModalControls} from '#/state/modals' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import { UsePreferencesQueryResponse, usePreferencesQuery, @@ -68,6 +70,7 @@ export function FeedSourceCardLoaded({ showLikes?: boolean }) { const pal = usePalette('default') + const {_} = useLingui() const navigation = useNavigation<NavigationProp>() const {openModal} = useModalControls() @@ -85,8 +88,8 @@ export function FeedSourceCardLoaded({ if (isSaved) { openModal({ name: 'confirm', - title: 'Remove from my feeds', - message: `Remove ${feed?.displayName} from my feeds?`, + title: _(msg`Remove from my feeds`), + message: _(msg`Remove ${feed.displayName} from my feeds?`), onPressConfirm: async () => { try { await removeFeed({uri: feed.uri}) @@ -107,7 +110,7 @@ export function FeedSourceCardLoaded({ logger.error('Failed to save feed', {error: e}) } } - }, [isSaved, openModal, feed, removeFeed, saveFeed]) + }, [isSaved, openModal, feed, removeFeed, saveFeed, _]) if (!feed || !preferences) return null diff --git a/src/view/com/modals/SwitchAccount.tsx b/src/view/com/modals/SwitchAccount.tsx index 3481b861c..dab0127cc 100644 --- a/src/view/com/modals/SwitchAccount.tsx +++ b/src/view/com/modals/SwitchAccount.tsx @@ -75,7 +75,7 @@ function SwitchAccountCard({account}: {account: SessionAccount}) { did: currentAccount.did, handle: currentAccount.handle, })} - title="Your profile" + title={_(msg`Your profile`)} noFeedback> {contents} </Link> diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx index 94fe7ff2d..75d44a2fc 100644 --- a/src/view/com/notifications/FeedItem.tsx +++ b/src/view/com/notifications/FeedItem.tsx @@ -235,7 +235,8 @@ let FeedItem = ({ {authors.length > 1 ? ( <> <Text style={[pal.text, s.mr5, s.ml5]}> - <Trans>and</Trans> + {' '} + <Trans>and</Trans>{' '} </Text> <Text style={[pal.text, s.bold]}> {formatCount(authors.length - 1)}{' '} diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index b5347fc84..1c9f2c16f 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -220,7 +220,7 @@ function PostThreadLoaded({ const renderItem = React.useCallback( ({item, index}: {item: YieldedItem; index: number}) => { if (item === TOP_COMPONENT) { - return isTablet ? <ViewHeader title="Post" /> : null + return isTablet ? <ViewHeader title={_(msg`Post`)} /> : null } else if (item === PARENT_SPINNER) { return ( <View style={styles.parentSpinner}> diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index f66c01d85..a4534b887 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -35,7 +35,8 @@ import {TimeElapsed} from 'view/com/util/TimeElapsed' import {makeProfileLink} from 'lib/routes/links' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {MAX_POST_LINES} from 'lib/constants' -import {Trans} from '@lingui/macro' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useLanguagePrefs} from '#/state/preferences' import {useComposerControls} from '#/state/shell/composer' import {useModerationOpts} from '#/state/queries/preferences' @@ -637,13 +638,14 @@ function ExpandedPostDetails({ translatorUrl: string }) { const pal = usePalette('default') + const {_} = useLingui() return ( <View style={[s.flexRow, s.mt2, s.mb10]}> <Text style={pal.textLight}>{niceDate(post.indexedAt)}</Text> {needsTranslation && ( <> <Text style={[pal.textLight, s.ml5, s.mr5]}>•</Text> - <Link href={translatorUrl} title="Translate"> + <Link href={translatorUrl} title={_(msg`Translate`)}> <Text style={pal.link}> <Trans>Translate</Trans> </Text> diff --git a/src/view/com/posts/FeedErrorMessage.tsx b/src/view/com/posts/FeedErrorMessage.tsx index 0ace06e9a..5a9290f66 100644 --- a/src/view/com/posts/FeedErrorMessage.tsx +++ b/src/view/com/posts/FeedErrorMessage.tsx @@ -10,6 +10,8 @@ import {useNavigation} from '@react-navigation/native' import {NavigationProp} from 'lib/routes/types' import {logger} from '#/logger' import {useModalControls} from '#/state/modals' +import {msg as msgLingui} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {FeedDescriptor} from '#/state/queries/post-feed' import {EmptyState} from '../util/EmptyState' import {cleanError} from '#/lib/strings/errors' @@ -86,6 +88,7 @@ function FeedgenErrorMessage({ knownError: KnownError }) { const pal = usePalette('default') + const {_: _l} = useLingui() const navigation = useNavigation<NavigationProp>() const msg = MESSAGES[knownError] const [_, uri] = feedDesc.split('|') @@ -100,8 +103,8 @@ function FeedgenErrorMessage({ const onRemoveFeed = React.useCallback(async () => { openModal({ name: 'confirm', - title: 'Remove feed', - message: 'Remove this feed from your saved feeds?', + title: _l(msgLingui`Remove feed`), + message: _l(msgLingui`Remove this feed from your saved feeds?`), async onPressConfirm() { try { await removeFeed({uri}) @@ -116,7 +119,7 @@ function FeedgenErrorMessage({ closeModal() }, }) - }, [openModal, closeModal, uri, removeFeed]) + }, [openModal, closeModal, uri, removeFeed, _l]) return ( <View diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index ab6667816..45998406c 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -236,9 +236,10 @@ let ProfileHeaderLoaded = ({ track('ProfileHeader:BlockAccountButtonClicked') openModal({ name: 'confirm', - title: 'Block Account', - message: - 'Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.', + title: _(msg`Block Account`), + message: _( + msg`Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, + ), onPressConfirm: async () => { try { await queueBlock() @@ -251,15 +252,16 @@ let ProfileHeaderLoaded = ({ } }, }) - }, [track, queueBlock, openModal]) + }, [track, queueBlock, openModal, _]) const onPressUnblockAccount = React.useCallback(async () => { track('ProfileHeader:UnblockAccountButtonClicked') openModal({ name: 'confirm', - title: 'Unblock Account', - message: - 'The account will be able to interact with you after unblocking.', + title: _(msg`Unblock Account`), + message: _( + msg`The account will be able to interact with you after unblocking.`, + ), onPressConfirm: async () => { try { await queueUnblock() @@ -272,7 +274,7 @@ let ProfileHeaderLoaded = ({ } }, }) - }, [track, queueUnblock, openModal]) + }, [track, queueUnblock, openModal, _]) const onPressReportAccount = React.useCallback(() => { track('ProfileHeader:ReportAccountButtonClicked') @@ -290,7 +292,7 @@ let ProfileHeaderLoaded = ({ let items: DropdownItem[] = [ { testID: 'profileHeaderDropdownShareBtn', - label: 'Share', + label: _(msg`Share`), onPress: onPressShare, icon: { ios: { @@ -304,7 +306,7 @@ let ProfileHeaderLoaded = ({ items.push({label: 'separator'}) items.push({ testID: 'profileHeaderDropdownListAddRemoveBtn', - label: 'Add to Lists', + label: _(msg`Add to Lists`), onPress: onPressAddRemoveLists, icon: { ios: { @@ -318,7 +320,9 @@ let ProfileHeaderLoaded = ({ if (!profile.viewer?.blocking) { items.push({ testID: 'profileHeaderDropdownMuteBtn', - label: profile.viewer?.muted ? 'Unmute Account' : 'Mute Account', + label: profile.viewer?.muted + ? _(msg`Unmute Account`) + : _(msg`Mute Account`), onPress: profile.viewer?.muted ? onPressUnmuteAccount : onPressMuteAccount, @@ -334,7 +338,9 @@ let ProfileHeaderLoaded = ({ if (!profile.viewer?.blockingByList) { items.push({ testID: 'profileHeaderDropdownBlockBtn', - label: profile.viewer?.blocking ? 'Unblock Account' : 'Block Account', + label: profile.viewer?.blocking + ? _(msg`Unblock Account`) + : _(msg`Block Account`), onPress: profile.viewer?.blocking ? onPressUnblockAccount : onPressBlockAccount, @@ -349,7 +355,7 @@ let ProfileHeaderLoaded = ({ } items.push({ testID: 'profileHeaderDropdownReportBtn', - label: 'Report Account', + label: _(msg`Report Account`), onPress: onPressReportAccount, icon: { ios: { @@ -373,6 +379,7 @@ let ProfileHeaderLoaded = ({ onPressBlockAccount, onPressReportAccount, onPressAddRemoveLists, + _, ]) const blockHide = diff --git a/src/view/com/util/AccountDropdownBtn.tsx b/src/view/com/util/AccountDropdownBtn.tsx index 96ce678ff..76d493886 100644 --- a/src/view/com/util/AccountDropdownBtn.tsx +++ b/src/view/com/util/AccountDropdownBtn.tsx @@ -19,7 +19,7 @@ export function AccountDropdownBtn({account}: {account: SessionAccount}) { const items: DropdownItem[] = [ { - label: 'Remove account', + label: _(msg`Remove account`), onPress: () => { removeAccount(account) Toast.show('Account removed from quick access') diff --git a/src/view/com/util/ErrorBoundary.tsx b/src/view/com/util/ErrorBoundary.tsx index 529435cf1..397588cfb 100644 --- a/src/view/com/util/ErrorBoundary.tsx +++ b/src/view/com/util/ErrorBoundary.tsx @@ -1,6 +1,7 @@ import React, {Component, ErrorInfo, ReactNode} from 'react' import {ErrorScreen} from './error/ErrorScreen' import {CenteredView} from './Views' +import {t} from '@lingui/macro' interface Props { children?: ReactNode @@ -30,8 +31,8 @@ export class ErrorBoundary extends Component<Props, State> { return ( <CenteredView style={{height: '100%', flex: 1}}> <ErrorScreen - title="Oh no!" - message="There was an unexpected issue in the application. Please let us know if this happened to you!" + title={t`Oh no!`} + message={t`There was an unexpected issue in the application. Please let us know if this happened to you!`} details={this.state.error.toString()} /> </CenteredView> diff --git a/src/view/com/util/TimeElapsed.tsx b/src/view/com/util/TimeElapsed.tsx index dad46448c..aa3a09223 100644 --- a/src/view/com/util/TimeElapsed.tsx +++ b/src/view/com/util/TimeElapsed.tsx @@ -3,7 +3,6 @@ import {ago} from 'lib/strings/time' import {useTickEveryMinute} from '#/state/shell' // FIXME(dan): Figure out why the false positives -/* eslint-disable react/prop-types */ export function TimeElapsed({ timestamp, diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index 0ad2c23e7..2012f5828 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -208,7 +208,7 @@ export function EditableUserAvatar({ [ !isWeb && { testID: 'changeAvatarCameraBtn', - label: 'Camera', + label: _(msg`Camera`), icon: { ios: { name: 'camera', @@ -232,7 +232,7 @@ export function EditableUserAvatar({ }, { testID: 'changeAvatarLibraryBtn', - label: 'Library', + label: _(msg`Library`), icon: { ios: { name: 'photo.on.rectangle.angled', @@ -269,7 +269,7 @@ export function EditableUserAvatar({ }, !!avatar && { testID: 'changeAvatarRemoveBtn', - label: 'Remove', + label: _(msg`Remove`), icon: { ios: { name: 'trash', @@ -287,6 +287,7 @@ export function EditableUserAvatar({ onSelectNewAvatar, requestCameraAccessIfNeeded, requestPhotoAccessIfNeeded, + _, ], ) diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx index 2b7915527..b31d7e551 100644 --- a/src/view/com/util/UserBanner.tsx +++ b/src/view/com/util/UserBanner.tsx @@ -35,7 +35,7 @@ export function UserBanner({ [ !isWeb && { testID: 'changeBannerCameraBtn', - label: 'Camera', + label: _(msg`Camera`), icon: { ios: { name: 'camera', @@ -57,7 +57,7 @@ export function UserBanner({ }, { testID: 'changeBannerLibraryBtn', - label: 'Library', + label: _(msg`Library`), icon: { ios: { name: 'photo.on.rectangle.angled', @@ -86,7 +86,7 @@ export function UserBanner({ }, !!banner && { testID: 'changeBannerRemoveBtn', - label: 'Remove', + label: _(msg`Remove`), icon: { ios: { name: 'trash', @@ -104,6 +104,7 @@ export function UserBanner({ onSelectNewBanner, requestCameraAccessIfNeeded, requestPhotoAccessIfNeeded, + _, ], ) diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index 90a165ce7..889839d07 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -20,6 +20,8 @@ import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads' import {useLanguagePrefs} from '#/state/preferences' import {logger} from '#/logger' import {Shadow} from '#/state/cache/types' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useSession} from '#/state/session' export function PostDropdownBtn({ @@ -35,6 +37,7 @@ export function PostDropdownBtn({ }) { const {currentAccount} = useSession() const theme = useTheme() + const {_} = useLingui() const defaultCtrlColor = theme.palette.default.postCtrl const {openModal} = useModalControls() const langPrefs = useLanguagePrefs() @@ -91,7 +94,7 @@ export function PostDropdownBtn({ const dropdownItems: NativeDropdownItem[] = [ { - label: 'Translate', + label: _(msg`Translate`), onPress() { onOpenTranslate() }, @@ -105,7 +108,7 @@ export function PostDropdownBtn({ }, }, { - label: 'Copy post text', + label: _(msg`Copy post text`), onPress() { onCopyPostText() }, @@ -119,7 +122,7 @@ export function PostDropdownBtn({ }, }, { - label: 'Share', + label: _(msg`Share`), onPress() { const url = toShareUrl(href) shareUrl(url) @@ -137,7 +140,7 @@ export function PostDropdownBtn({ label: 'separator', }, { - label: isThreadMuted ? 'Unmute thread' : 'Mute thread', + label: isThreadMuted ? _(msg`Unmute thread`) : _(msg`Mute thread`), onPress() { onToggleThreadMute() }, @@ -154,7 +157,7 @@ export function PostDropdownBtn({ label: 'separator', }, !isAuthor && { - label: 'Report post', + label: _(msg`Report post`), onPress() { openModal({ name: 'report', @@ -175,12 +178,12 @@ export function PostDropdownBtn({ label: 'separator', }, isAuthor && { - label: 'Delete post', + label: _(msg`Delete post`), onPress() { openModal({ name: 'confirm', - title: 'Delete this post?', - message: 'Are you sure? This can not be undone.', + title: _(msg`Delete this post?`), + message: _(msg`Are you sure? This cannot be undone.`), onPressConfirm: onDeletePost, }) }, diff --git a/src/view/com/util/post-ctrls/RepostButton.web.tsx b/src/view/com/util/post-ctrls/RepostButton.web.tsx index 70f7229d4..6c5f816aa 100644 --- a/src/view/com/util/post-ctrls/RepostButton.web.tsx +++ b/src/view/com/util/post-ctrls/RepostButton.web.tsx @@ -41,7 +41,7 @@ export const RepostButton = ({ const dropdownItems: NativeDropdownItem[] = [ { - label: isReposted ? 'Undo repost' : 'Repost', + label: isReposted ? _(msg`Undo repost`) : _(msg`Repost`), testID: 'repostDropdownRepostBtn', icon: { ios: {name: 'repeat'}, @@ -51,7 +51,7 @@ export const RepostButton = ({ onPress: onRepost, }, { - label: 'Quote post', + label: _(msg`Quote post`), testID: 'repostDropdownQuoteBtn', icon: { ios: {name: 'quote.bubble'}, diff --git a/src/view/screens/AppPasswords.tsx b/src/view/screens/AppPasswords.tsx index b2eee392a..bc77a48cd 100644 --- a/src/view/screens/AppPasswords.tsx +++ b/src/view/screens/AppPasswords.tsx @@ -183,9 +183,10 @@ export const AppPasswords = withAuthRequired( function AppPasswordsHeader() { const {isTabletOrDesktop} = useWebMediaQueries() const pal = usePalette('default') + const {_} = useLingui() return ( <> - <ViewHeader title="App Passwords" showOnDesktop /> + <ViewHeader title={_(msg`App Passwords`)} showOnDesktop /> <Text type="sm" style={[ @@ -220,14 +221,16 @@ function AppPassword({ const onDelete = React.useCallback(async () => { openModal({ name: 'confirm', - title: 'Delete App Password', - message: `Are you sure you want to delete the app password "${name}"?`, + title: _(msg`Delete app password`), + message: _( + msg`Are you sure you want to delete the app password "${name}"?`, + ), async onPressConfirm() { await deleteMutation.mutateAsync({name}) Toast.show('App password deleted') }, }) - }, [deleteMutation, openModal, name]) + }, [deleteMutation, openModal, name, _]) const primaryLocale = contentLanguages.length > 0 ? contentLanguages[0] : 'en-US' @@ -245,15 +248,17 @@ function AppPassword({ {name} </Text> <Text type="md" style={[pal.text, styles.pr10]} numberOfLines={1}> - Created{' '} - {Intl.DateTimeFormat(primaryLocale, { - year: 'numeric', - month: 'numeric', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }).format(new Date(createdAt))} + <Trans> + Created{' '} + {Intl.DateTimeFormat(primaryLocale, { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }).format(new Date(createdAt))} + </Trans> </Text> </View> <FontAwesomeIcon icon={['far', 'trash-can']} style={styles.trashIcon} /> diff --git a/src/view/screens/CommunityGuidelines.tsx b/src/view/screens/CommunityGuidelines.tsx index 712172c3b..1931c6f13 100644 --- a/src/view/screens/CommunityGuidelines.tsx +++ b/src/view/screens/CommunityGuidelines.tsx @@ -9,6 +9,8 @@ import {ScrollView} from 'view/com/util/Views' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps< CommonNavigatorParams, @@ -16,6 +18,7 @@ type Props = NativeStackScreenProps< > export const CommunityGuidelinesScreen = (_props: Props) => { const pal = usePalette('default') + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() useFocusEffect( @@ -26,16 +29,18 @@ export const CommunityGuidelinesScreen = (_props: Props) => { return ( <View> - <ViewHeader title="Community Guidelines" /> + <ViewHeader title={_(msg`Community Guidelines`)} /> <ScrollView style={[s.hContentRegion, pal.view]}> <View style={[s.p20]}> <Text style={pal.text}> - The Community Guidelines have been moved to{' '} - <TextLink - style={pal.link} - href="https://blueskyweb.xyz/support/community-guidelines" - text="blueskyweb.xyz/support/community-guidelines" - /> + <Trans> + The Community Guidelines have been moved to{' '} + <TextLink + style={pal.link} + href="https://blueskyweb.xyz/support/community-guidelines" + text="blueskyweb.xyz/support/community-guidelines" + /> + </Trans> </Text> </View> <View style={s.footerSpacer} /> diff --git a/src/view/screens/CopyrightPolicy.tsx b/src/view/screens/CopyrightPolicy.tsx index 816c1c1ee..2026f28c6 100644 --- a/src/view/screens/CopyrightPolicy.tsx +++ b/src/view/screens/CopyrightPolicy.tsx @@ -9,10 +9,13 @@ import {ScrollView} from 'view/com/util/Views' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'CopyrightPolicy'> export const CopyrightPolicyScreen = (_props: Props) => { const pal = usePalette('default') + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() useFocusEffect( @@ -23,16 +26,18 @@ export const CopyrightPolicyScreen = (_props: Props) => { return ( <View> - <ViewHeader title="Copyright Policy" /> + <ViewHeader title={_(msg`Copyright Policy`)} /> <ScrollView style={[s.hContentRegion, pal.view]}> <View style={[s.p20]}> <Text style={pal.text}> - The Copyright Policy has been moved to{' '} - <TextLink - style={pal.link} - href="https://blueskyweb.xyz/support/community-guidelines" - text="blueskyweb.xyz/support/community-guidelines" - /> + <Trans> + The Copyright Policy has been moved to{' '} + <TextLink + style={pal.link} + href="https://blueskyweb.xyz/support/community-guidelines" + text="blueskyweb.xyz/support/community-guidelines" + /> + </Trans> </Text> </View> <View style={s.footerSpacer} /> diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index a6d47f5ce..5d62125ce 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -467,7 +467,7 @@ export const FeedsScreen = withAuthRequired(function FeedsScreenImpl( <View style={[pal.view, styles.container]}> {isMobile && ( <ViewHeader - title="Feeds" + title={_(msg`Feeds`)} canGoBack={false} renderButton={renderHeaderBtn} showBorder diff --git a/src/view/screens/LanguageSettings.tsx b/src/view/screens/LanguageSettings.tsx index 649daea0e..7a2e54dc8 100644 --- a/src/view/screens/LanguageSettings.tsx +++ b/src/view/screens/LanguageSettings.tsx @@ -14,16 +14,19 @@ import { } from '@fortawesome/react-native-fontawesome' import {useAnalytics} from 'lib/analytics/analytics' import {useFocusEffect} from '@react-navigation/native' -import {LANGUAGES} from 'lib/../locale/languages' +import {APP_LANGUAGES, LANGUAGES} from 'lib/../locale/languages' import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select' import {useSetMinimalShellMode} from '#/state/shell' import {useModalControls} from '#/state/modals' import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'LanguageSettings'> -export function LanguageSettingsScreen(_: Props) { +export function LanguageSettingsScreen(_props: Props) { const pal = usePalette('default') + const {_} = useLingui() const langPrefs = useLanguagePrefs() const setLangPrefs = useLanguagePrefsApi() const {isTabletOrDesktop} = useWebMediaQueries() @@ -52,6 +55,15 @@ export function LanguageSettingsScreen(_: Props) { [langPrefs, setLangPrefs], ) + const onChangeAppLanguage = React.useCallback( + (value: Parameters<PickerSelectProps['onValueChange']>[0]) => { + if (langPrefs.appLanguage !== value) { + setLangPrefs.setAppLanguage(value) + } + }, + [langPrefs, setLangPrefs], + ) + const myLanguages = React.useMemo(() => { return ( langPrefs.contentLanguages @@ -71,15 +83,109 @@ export function LanguageSettingsScreen(_: Props) { styles.container, isTabletOrDesktop && styles.desktopContainer, ]}> - <ViewHeader title="Language Settings" showOnDesktop /> + <ViewHeader title={_(msg`Language Settings`)} showOnDesktop /> <View style={{paddingTop: 20, paddingHorizontal: 20}}> + {/* APP LANGUAGE */} + <View style={{paddingBottom: 20}}> + <Text type="title-sm" style={[pal.text, s.pb5]}> + <Trans>App Language</Trans> + </Text> + <Text style={[pal.text, s.pb10]}> + <Trans> + Select your app language for the default text to display in the + app + </Trans> + </Text> + + <View style={{position: 'relative'}}> + <RNPickerSelect + value={langPrefs.appLanguage} + onValueChange={onChangeAppLanguage} + items={APP_LANGUAGES.filter(l => Boolean(l.code2)).map(l => ({ + label: l.name, + value: l.code2, + key: l.code2, + }))} + style={{ + inputAndroid: { + backgroundColor: pal.viewLight.backgroundColor, + color: pal.text.color, + fontSize: 14, + letterSpacing: 0.5, + fontWeight: '500', + paddingHorizontal: 14, + paddingVertical: 8, + borderRadius: 24, + }, + inputIOS: { + backgroundColor: pal.viewLight.backgroundColor, + color: pal.text.color, + fontSize: 14, + letterSpacing: 0.5, + fontWeight: '500', + paddingHorizontal: 14, + paddingVertical: 8, + borderRadius: 24, + }, + inputWeb: { + // @ts-ignore web only + cursor: 'pointer', + '-moz-appearance': 'none', + '-webkit-appearance': 'none', + appearance: 'none', + outline: 0, + borderWidth: 0, + backgroundColor: pal.viewLight.backgroundColor, + color: pal.text.color, + fontSize: 14, + letterSpacing: 0.5, + fontWeight: '500', + paddingHorizontal: 14, + paddingVertical: 8, + borderRadius: 24, + }, + }} + /> + + <View + style={{ + position: 'absolute', + top: 1, + right: 1, + bottom: 1, + width: 40, + backgroundColor: pal.viewLight.backgroundColor, + borderRadius: 24, + pointerEvents: 'none', + alignItems: 'center', + justifyContent: 'center', + }}> + <FontAwesomeIcon + icon="chevron-down" + style={pal.text as FontAwesomeIconStyle} + /> + </View> + </View> + </View> + + <View + style={{ + height: 1, + backgroundColor: pal.border.borderColor, + marginBottom: 20, + }} + /> + + {/* PRIMARY LANGUAGE */} <View style={{paddingBottom: 20}}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Primary Language + <Trans>Primary Language</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Select your preferred language for translations in your feed. + <Trans> + Select your preferred language for translations in your feed. + </Trans> </Text> <View style={{position: 'relative'}}> @@ -161,13 +267,16 @@ export function LanguageSettingsScreen(_: Props) { }} /> + {/* CONTENT LANGUAGES */} <View style={{paddingBottom: 20}}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Content Languages + <Trans>Content Languages</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Select which languages you want your subscribed feeds to include. If - none are selected, all languages will be shown. + <Trans> + Select which languages you want your subscribed feeds to include. + If none are selected, all languages will be shown. + </Trans> </Text> <Button diff --git a/src/view/screens/Lists.tsx b/src/view/screens/Lists.tsx index 00711784d..c97be4a02 100644 --- a/src/view/screens/Lists.tsx +++ b/src/view/screens/Lists.tsx @@ -15,6 +15,7 @@ import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader' import {s} from 'lib/styles' import {useSetMinimalShellMode} from '#/state/shell' import {useModalControls} from '#/state/modals' +import {Trans} from '@lingui/macro' type Props = NativeStackScreenProps<CommonNavigatorParams, 'Lists'> export const ListsScreen = withAuthRequired( @@ -56,10 +57,10 @@ export const ListsScreen = withAuthRequired( }> <View style={{flex: 1}}> <Text type="title-lg" style={[pal.text, {fontWeight: 'bold'}]}> - User Lists + <Trans>User Lists</Trans> </Text> <Text style={pal.textLight}> - Public, shareable lists which can drive feeds. + <Trans>Public, shareable lists which can drive feeds.</Trans> </Text> </View> <View> @@ -74,7 +75,7 @@ export const ListsScreen = withAuthRequired( }}> <FontAwesomeIcon icon="plus" color={pal.colors.text} /> <Text type="button" style={pal.text}> - New + <Trans>New</Trans> </Text> </Button> </View> diff --git a/src/view/screens/Moderation.tsx b/src/view/screens/Moderation.tsx index 37eecf22d..10b72fe9e 100644 --- a/src/view/screens/Moderation.tsx +++ b/src/view/screens/Moderation.tsx @@ -17,11 +17,14 @@ import {useAnalytics} from 'lib/analytics/analytics' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useSetMinimalShellMode} from '#/state/shell' import {useModalControls} from '#/state/modals' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'Moderation'> export const ModerationScreen = withAuthRequired( function Moderation({}: Props) { const pal = usePalette('default') + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const {screen, track} = useAnalytics() const {isTabletOrDesktop} = useWebMediaQueries() @@ -47,7 +50,7 @@ export const ModerationScreen = withAuthRequired( isTabletOrDesktop ? styles.desktopContainer : pal.viewLight, ]} testID="moderationScreen"> - <ViewHeader title="Moderation" showOnDesktop /> + <ViewHeader title={_(msg`Moderation`)} showOnDesktop /> <View style={styles.spacer} /> <TouchableOpacity testID="contentFilteringBtn" @@ -63,7 +66,7 @@ export const ModerationScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - Content filtering + <Trans>Content filtering</Trans> </Text> </TouchableOpacity> <Link @@ -77,7 +80,7 @@ export const ModerationScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - Moderation lists + <Trans>Moderation lists</Trans> </Text> </Link> <Link @@ -91,7 +94,7 @@ export const ModerationScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - Muted accounts + <Trans>Muted accounts</Trans> </Text> </Link> <Link @@ -105,7 +108,7 @@ export const ModerationScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - Blocked accounts + <Trans>Blocked accounts</Trans> </Text> </Link> </CenteredView> diff --git a/src/view/screens/ModerationBlockedAccounts.tsx b/src/view/screens/ModerationBlockedAccounts.tsx index 702a8d44e..1c592dde8 100644 --- a/src/view/screens/ModerationBlockedAccounts.tsx +++ b/src/view/screens/ModerationBlockedAccounts.tsx @@ -21,6 +21,8 @@ import {ErrorScreen} from '../com/util/error/ErrorScreen' import {ProfileCard} from 'view/com/profile/ProfileCard' import {logger} from '#/logger' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useMyBlockedAccountsQuery} from '#/state/queries/my-blocked-accounts' import {cleanError} from '#/lib/strings/errors' @@ -31,6 +33,7 @@ type Props = NativeStackScreenProps< export const ModerationBlockedAccounts = withAuthRequired( function ModerationBlockedAccountsImpl({}: Props) { const pal = usePalette('default') + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const {isTabletOrDesktop} = useWebMediaQueries() const {screen} = useAnalytics() @@ -104,7 +107,7 @@ export const ModerationBlockedAccounts = withAuthRequired( pal.border, ]} testID="blockedAccountsScreen"> - <ViewHeader title="Blocked Accounts" showOnDesktop /> + <ViewHeader title={_(msg`Blocked Accounts`)} showOnDesktop /> <Text type="sm" style={[ @@ -112,9 +115,11 @@ export const ModerationBlockedAccounts = withAuthRequired( pal.text, isTabletOrDesktop && styles.descriptionDesktop, ]}> - Blocked accounts cannot reply in your threads, mention you, or - otherwise interact with you. You will not see their content and they - will be prevented from seeing yours. + <Trans> + Blocked accounts cannot reply in your threads, mention you, or + otherwise interact with you. You will not see their content and they + will be prevented from seeing yours. + </Trans> </Text> {isEmpty ? ( <View style={[pal.border, !isTabletOrDesktop && styles.flex1]}> @@ -127,9 +132,11 @@ export const ModerationBlockedAccounts = withAuthRequired( ) : ( <View style={[styles.empty, pal.viewLight]}> <Text type="lg" style={[pal.text, styles.emptyText]}> - You have not blocked any accounts yet. To block an account, go - to their profile and selected "Block account" from the menu on - their account. + <Trans> + You have not blocked any accounts yet. To block an account, + go to their profile and selected "Block account" from the + menu on their account. + </Trans> </Text> </View> )} diff --git a/src/view/screens/ModerationMutedAccounts.tsx b/src/view/screens/ModerationMutedAccounts.tsx index fe0b4bf14..36bcbf1fa 100644 --- a/src/view/screens/ModerationMutedAccounts.tsx +++ b/src/view/screens/ModerationMutedAccounts.tsx @@ -21,6 +21,8 @@ import {ErrorScreen} from '../com/util/error/ErrorScreen' import {ProfileCard} from 'view/com/profile/ProfileCard' import {logger} from '#/logger' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useMyMutedAccountsQuery} from '#/state/queries/my-muted-accounts' import {cleanError} from '#/lib/strings/errors' @@ -31,6 +33,7 @@ type Props = NativeStackScreenProps< export const ModerationMutedAccounts = withAuthRequired( function ModerationMutedAccountsImpl({}: Props) { const pal = usePalette('default') + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const {isTabletOrDesktop} = useWebMediaQueries() const {screen} = useAnalytics() @@ -104,7 +107,7 @@ export const ModerationMutedAccounts = withAuthRequired( pal.border, ]} testID="mutedAccountsScreen"> - <ViewHeader title="Muted Accounts" showOnDesktop /> + <ViewHeader title={_(msg`Muted Accounts`)} showOnDesktop /> <Text type="sm" style={[ @@ -112,8 +115,10 @@ export const ModerationMutedAccounts = withAuthRequired( pal.text, isTabletOrDesktop && styles.descriptionDesktop, ]}> - Muted accounts have their posts removed from your feed and from your - notifications. Mutes are completely private. + <Trans> + Muted accounts have their posts removed from your feed and from your + notifications. Mutes are completely private. + </Trans> </Text> {isEmpty ? ( <View style={[pal.border, !isTabletOrDesktop && styles.flex1]}> @@ -126,9 +131,11 @@ export const ModerationMutedAccounts = withAuthRequired( ) : ( <View style={[styles.empty, pal.viewLight]}> <Text type="lg" style={[pal.text, styles.emptyText]}> - You have not muted any accounts yet. To mute an account, go to - their profile and selected "Mute account" from the menu on - their account. + <Trans> + You have not muted any accounts yet. To mute an account, go + to their profile and selected "Mute account" from the menu + on their account. + </Trans> </Text> </View> )} diff --git a/src/view/screens/NotFound.tsx b/src/view/screens/NotFound.tsx index c2125756c..2508a9ed2 100644 --- a/src/view/screens/NotFound.tsx +++ b/src/view/screens/NotFound.tsx @@ -12,9 +12,12 @@ import {NavigationProp} from 'lib/routes/types' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export const NotFoundScreen = () => { const pal = usePalette('default') + const {_} = useLingui() const navigation = useNavigation<NavigationProp>() const setMinimalShellMode = useSetMinimalShellMode() @@ -36,13 +39,15 @@ export const NotFoundScreen = () => { return ( <View testID="notFoundView" style={pal.view}> - <ViewHeader title="Page not found" /> + <ViewHeader title={_(msg`Page not found`)} /> <View style={styles.container}> <Text type="title-2xl" style={[pal.text, s.mb10]}> - Page not found + <Trans>Page not found</Trans> </Text> <Text type="md" style={[pal.text, s.mb10]}> - We're sorry! We can't find the page you were looking for. + <Trans> + We're sorry! We can't find the page you were looking for. + </Trans> </Text> <Button type="primary" diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index c892ee4e3..4ed9c7f74 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -18,6 +18,8 @@ import {s, colors} from 'lib/styles' import {useAnalytics} from 'lib/analytics/analytics' import {logger} from '#/logger' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed' import {listenSoftReset, emitSoftReset} from '#/state/events' @@ -28,6 +30,7 @@ type Props = NativeStackScreenProps< > export const NotificationsScreen = withAuthRequired( function NotificationsScreenImpl({}: Props) { + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll() const scrollElRef = React.useRef<FlatList>(null) @@ -83,7 +86,7 @@ export const NotificationsScreen = withAuthRequired( style={[pal.text, {fontWeight: 'bold'}]} text={ <> - Notifications{' '} + <Trans>Notifications</Trans>{' '} {hasNew && ( <View style={{ @@ -107,7 +110,7 @@ export const NotificationsScreen = withAuthRequired( return ( <View testID="notificationsScreen" style={s.hContentRegion}> - <ViewHeader title="Notifications" canGoBack={false} /> + <ViewHeader title={_(msg`Notifications`)} canGoBack={false} /> <Feed onScroll={onMainScroll} scrollElRef={scrollElRef} @@ -116,7 +119,7 @@ export const NotificationsScreen = withAuthRequired( {(isScrolledDown || hasNew) && ( <LoadLatestBtn onPress={onPressLoadLatest} - label="Load new notifications" + label={_(msg`Load new notifications`)} showIndicator={hasNew} /> )} diff --git a/src/view/screens/PostLikedBy.tsx b/src/view/screens/PostLikedBy.tsx index 2f45908b3..ab7bbcefe 100644 --- a/src/view/screens/PostLikedBy.tsx +++ b/src/view/screens/PostLikedBy.tsx @@ -7,12 +7,15 @@ import {ViewHeader} from '../com/util/ViewHeader' import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy' import {makeRecordUri} from 'lib/strings/url-helpers' import {useSetMinimalShellMode} from '#/state/shell' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'> export const PostLikedByScreen = withAuthRequired(({route}: Props) => { const setMinimalShellMode = useSetMinimalShellMode() const {name, rkey} = route.params const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey) + const {_} = useLingui() useFocusEffect( React.useCallback(() => { @@ -22,7 +25,7 @@ export const PostLikedByScreen = withAuthRequired(({route}: Props) => { return ( <View> - <ViewHeader title="Liked by" /> + <ViewHeader title={_(msg`Liked by`)} /> <PostLikedByComponent uri={uri} /> </View> ) diff --git a/src/view/screens/PostRepostedBy.tsx b/src/view/screens/PostRepostedBy.tsx index abe03467a..eabbc4a4e 100644 --- a/src/view/screens/PostRepostedBy.tsx +++ b/src/view/screens/PostRepostedBy.tsx @@ -7,12 +7,15 @@ import {ViewHeader} from '../com/util/ViewHeader' import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy' import {makeRecordUri} from 'lib/strings/url-helpers' import {useSetMinimalShellMode} from '#/state/shell' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'> export const PostRepostedByScreen = withAuthRequired(({route}: Props) => { const {name, rkey} = route.params const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey) const setMinimalShellMode = useSetMinimalShellMode() + const {_} = useLingui() useFocusEffect( React.useCallback(() => { @@ -22,7 +25,7 @@ export const PostRepostedByScreen = withAuthRequired(({route}: Props) => { return ( <View> - <ViewHeader title="Reposted by" /> + <ViewHeader title={_(msg`Reposted by`)} /> <PostRepostedByComponent uri={uri} /> </View> ) diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx index 844a96d11..0476e182b 100644 --- a/src/view/screens/PostThread.tsx +++ b/src/view/screens/PostThread.tsx @@ -19,6 +19,8 @@ import {clamp} from 'lodash' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' import {useSetMinimalShellMode} from '#/state/shell' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useResolveUriQuery} from '#/state/queries/resolve-uri' import {ErrorMessage} from '../com/util/error/ErrorMessage' import {CenteredView} from '../com/util/Views' @@ -29,6 +31,7 @@ export const PostThreadScreen = withAuthRequired(function PostThreadScreenImpl({ route, }: Props) { const queryClient = useQueryClient() + const {_} = useLingui() const {fabMinimalShellTransform} = useMinimalShellMode() const setMinimalShellMode = useSetMinimalShellMode() const {openComposer} = useComposerControls() @@ -74,7 +77,7 @@ export const PostThreadScreen = withAuthRequired(function PostThreadScreenImpl({ return ( <View style={s.hContentRegion}> - {isMobile && <ViewHeader title="Post" />} + {isMobile && <ViewHeader title={_(msg`Post`)} />} <View style={s.flex1}> {uriError ? ( <CenteredView> diff --git a/src/view/screens/PreferencesHomeFeed.tsx b/src/view/screens/PreferencesHomeFeed.tsx index 2fd0eff37..fe17be5e8 100644 --- a/src/view/screens/PreferencesHomeFeed.tsx +++ b/src/view/screens/PreferencesHomeFeed.tsx @@ -92,7 +92,7 @@ export function PreferencesHomeFeed({navigation}: Props) { styles.container, isTabletOrDesktop && styles.desktopContainer, ]}> - <ViewHeader title="Home Feed Preferences" showOnDesktop /> + <ViewHeader title={_(msg`Home Feed Preferences`)} showOnDesktop /> <View style={[ styles.titleSection, @@ -142,7 +142,7 @@ export function PreferencesHomeFeed({navigation}: Props) { </Text> <ToggleButton type="default-light" - label="Followed users only" + label={_(msg`Followed users only`)} isSelected={Boolean( variables?.hideRepliesByUnfollowed ?? preferences?.feedViewPrefs?.hideRepliesByUnfollowed, @@ -188,8 +188,8 @@ export function PreferencesHomeFeed({navigation}: Props) { label={ variables?.hideReposts ?? preferences?.feedViewPrefs?.hideReposts - ? 'No' - : 'Yes' + ? _(msg`No`) + : _(msg`Yes`) } isSelected={ !( @@ -223,8 +223,8 @@ export function PreferencesHomeFeed({navigation}: Props) { label={ variables?.hideQuotePosts ?? preferences?.feedViewPrefs?.hideQuotePosts - ? 'No' - : 'Yes' + ? _(msg`No`) + : _(msg`Yes`) } isSelected={ !( @@ -259,8 +259,8 @@ export function PreferencesHomeFeed({navigation}: Props) { label={ variables?.lab_mergeFeedEnabled ?? preferences?.feedViewPrefs?.lab_mergeFeedEnabled - ? 'Yes' - : 'No' + ? _(msg`Yes`) + : _(msg`No`) } isSelected={ !!( diff --git a/src/view/screens/PreferencesThreads.tsx b/src/view/screens/PreferencesThreads.tsx index 7bd87b712..73d941932 100644 --- a/src/view/screens/PreferencesThreads.tsx +++ b/src/view/screens/PreferencesThreads.tsx @@ -50,7 +50,7 @@ export function PreferencesThreads({navigation}: Props) { styles.container, isTabletOrDesktop && styles.desktopContainer, ]}> - <ViewHeader title="Thread Preferences" showOnDesktop /> + <ViewHeader title={_(msg`Thread Preferences`)} showOnDesktop /> <View style={[ styles.titleSection, diff --git a/src/view/screens/PrivacyPolicy.tsx b/src/view/screens/PrivacyPolicy.tsx index f709c9fda..247afc316 100644 --- a/src/view/screens/PrivacyPolicy.tsx +++ b/src/view/screens/PrivacyPolicy.tsx @@ -9,10 +9,13 @@ import {ScrollView} from 'view/com/util/Views' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'> export const PrivacyPolicyScreen = (_props: Props) => { const pal = usePalette('default') + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() useFocusEffect( @@ -23,16 +26,18 @@ export const PrivacyPolicyScreen = (_props: Props) => { return ( <View> - <ViewHeader title="Privacy Policy" /> + <ViewHeader title={_(msg`Privacy Policy`)} /> <ScrollView style={[s.hContentRegion, pal.view]}> <View style={[s.p20]}> <Text style={pal.text}> - The Privacy Policy has been moved to{' '} - <TextLink - style={pal.link} - href="https://blueskyweb.xyz/support/privacy-policy" - text="blueskyweb.xyz/support/privacy-policy" - /> + <Trans> + The Privacy Policy has been moved to{' '} + <TextLink + style={pal.link} + href="https://blueskyweb.xyz/support/privacy-policy" + text="blueskyweb.xyz/support/privacy-policy" + /> + </Trans> </Text> </View> <View style={s.footerSpacer} /> diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx index 62f5f1b36..3974d3a11 100644 --- a/src/view/screens/ProfileFeed.tsx +++ b/src/view/screens/ProfileFeed.tsx @@ -269,7 +269,7 @@ export function ProfileFeedScreenInner({ return [ { testID: 'feedHeaderDropdownToggleSavedBtn', - label: isSaved ? 'Remove from my feeds' : 'Add to my feeds', + label: isSaved ? _(msg`Remove from my feeds`) : _(msg`Add to my feeds`), onPress: isSavePending || isRemovePending ? undefined : onToggleSaved, icon: isSaved ? { @@ -289,7 +289,7 @@ export function ProfileFeedScreenInner({ }, { testID: 'feedHeaderDropdownReportBtn', - label: 'Report feed', + label: _(msg`Report feed`), onPress: onPressReport, icon: { ios: { @@ -301,7 +301,7 @@ export function ProfileFeedScreenInner({ }, { testID: 'feedHeaderDropdownShareBtn', - label: 'Share link', + label: _(msg`Share feed`), onPress: onPressShare, icon: { ios: { @@ -319,6 +319,7 @@ export function ProfileFeedScreenInner({ isSaved, isSavePending, isRemovePending, + _, ]) const renderHeader = useCallback(() => { diff --git a/src/view/screens/ProfileFeedLikedBy.tsx b/src/view/screens/ProfileFeedLikedBy.tsx index 4972116f3..c8466360e 100644 --- a/src/view/screens/ProfileFeedLikedBy.tsx +++ b/src/view/screens/ProfileFeedLikedBy.tsx @@ -7,12 +7,15 @@ import {ViewHeader} from '../com/util/ViewHeader' import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy' import {makeRecordUri} from 'lib/strings/url-helpers' import {useSetMinimalShellMode} from '#/state/shell' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFeedLikedBy'> export const ProfileFeedLikedByScreen = withAuthRequired(({route}: Props) => { const setMinimalShellMode = useSetMinimalShellMode() const {name, rkey} = route.params const uri = makeRecordUri(name, 'app.bsky.feed.generator', rkey) + const {_} = useLingui() useFocusEffect( React.useCallback(() => { @@ -22,7 +25,7 @@ export const ProfileFeedLikedByScreen = withAuthRequired(({route}: Props) => { return ( <View> - <ViewHeader title="Liked by" /> + <ViewHeader title={_(msg`Liked by`)} /> <PostLikedByComponent uri={uri} /> </View> ) diff --git a/src/view/screens/ProfileFollowers.tsx b/src/view/screens/ProfileFollowers.tsx index 49f55bf46..13e69541a 100644 --- a/src/view/screens/ProfileFollowers.tsx +++ b/src/view/screens/ProfileFollowers.tsx @@ -6,11 +6,14 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from '../com/util/ViewHeader' import {ProfileFollowers as ProfileFollowersComponent} from '../com/profile/ProfileFollowers' import {useSetMinimalShellMode} from '#/state/shell' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFollowers'> export const ProfileFollowersScreen = withAuthRequired(({route}: Props) => { const {name} = route.params const setMinimalShellMode = useSetMinimalShellMode() + const {_} = useLingui() useFocusEffect( React.useCallback(() => { @@ -20,7 +23,7 @@ export const ProfileFollowersScreen = withAuthRequired(({route}: Props) => { return ( <View> - <ViewHeader title="Followers" /> + <ViewHeader title={_(msg`Followers`)} /> <ProfileFollowersComponent name={name} /> </View> ) diff --git a/src/view/screens/ProfileFollows.tsx b/src/view/screens/ProfileFollows.tsx index 4f0ff7d67..07d6eaa78 100644 --- a/src/view/screens/ProfileFollows.tsx +++ b/src/view/screens/ProfileFollows.tsx @@ -6,11 +6,14 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from '../com/util/ViewHeader' import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows' import {useSetMinimalShellMode} from '#/state/shell' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFollows'> export const ProfileFollowsScreen = withAuthRequired(({route}: Props) => { const {name} = route.params const setMinimalShellMode = useSetMinimalShellMode() + const {_} = useLingui() useFocusEffect( React.useCallback(() => { @@ -20,7 +23,7 @@ export const ProfileFollowsScreen = withAuthRequired(({route}: Props) => { return ( <View> - <ViewHeader title="Following" /> + <ViewHeader title={_(msg`Following`)} /> <ProfileFollowsComponent name={name} /> </View> ) diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index ec6c7f79c..b5a650643 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -268,9 +268,10 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { const onSubscribeMute = useCallback(() => { openModal({ name: 'confirm', - title: 'Mute these accounts?', - message: - 'Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them.', + title: _(msg`Mute these accounts?`), + message: _( + msg`Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them.`, + ), confirmBtnText: 'Mute this List', async onPressConfirm() { try { @@ -286,7 +287,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { closeModal() }, }) - }, [openModal, closeModal, list, listMuteMutation]) + }, [openModal, closeModal, list, listMuteMutation, _]) const onUnsubscribeMute = useCallback(async () => { try { @@ -302,9 +303,10 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { const onSubscribeBlock = useCallback(() => { openModal({ name: 'confirm', - title: 'Block these accounts?', - message: - 'Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.', + title: _(msg`Block these accounts?`), + message: _( + msg`Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you.`, + ), confirmBtnText: 'Block this List', async onPressConfirm() { try { @@ -320,7 +322,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { closeModal() }, }) - }, [openModal, closeModal, list, listBlockMutation]) + }, [openModal, closeModal, list, listBlockMutation, _]) const onUnsubscribeBlock = useCallback(async () => { try { @@ -343,8 +345,8 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { const onPressDelete = useCallback(() => { openModal({ name: 'confirm', - title: 'Delete List', - message: 'Are you sure?', + title: _(msg`Delete List`), + message: _(msg`Are you sure?`), async onPressConfirm() { await listDeleteMutation.mutateAsync({uri: list.uri}) Toast.show('List deleted') @@ -355,7 +357,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { } }, }) - }, [openModal, list, listDeleteMutation, navigation]) + }, [openModal, list, listDeleteMutation, navigation, _]) const onPressReport = useCallback(() => { openModal({ @@ -374,7 +376,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { let items: DropdownItem[] = [ { testID: 'listHeaderDropdownShareBtn', - label: 'Share', + label: _(msg`Share`), onPress: onPressShare, icon: { ios: { @@ -389,7 +391,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { items.push({label: 'separator'}) items.push({ testID: 'listHeaderDropdownEditBtn', - label: 'Edit List Details', + label: _(msg`Edit list details`), onPress: onPressEdit, icon: { ios: { @@ -401,7 +403,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { }) items.push({ testID: 'listHeaderDropdownDeleteBtn', - label: 'Delete List', + label: _(msg`Delete List`), onPress: onPressDelete, icon: { ios: { @@ -415,7 +417,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { items.push({label: 'separator'}) items.push({ testID: 'listHeaderDropdownReportBtn', - label: 'Report List', + label: _(msg`Report List`), onPress: onPressReport, icon: { ios: { @@ -427,13 +429,13 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { }) } return items - }, [isOwner, onPressShare, onPressEdit, onPressDelete, onPressReport]) + }, [isOwner, onPressShare, onPressEdit, onPressDelete, onPressReport, _]) const subscribeDropdownItems: DropdownItem[] = useMemo(() => { return [ { testID: 'subscribeDropdownMuteBtn', - label: 'Mute accounts', + label: _(msg`Mute accounts`), onPress: onSubscribeMute, icon: { ios: { @@ -445,7 +447,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { }, { testID: 'subscribeDropdownBlockBtn', - label: 'Block accounts', + label: _(msg`Block accounts`), onPress: onSubscribeBlock, icon: { ios: { @@ -456,7 +458,7 @@ function Header({rkey, list}: {rkey: string; list: AppBskyGraphDefs.ListView}) { }, }, ] - }, [onSubscribeMute, onSubscribeBlock]) + }, [onSubscribeMute, onSubscribeBlock, _]) return ( <ProfileSubpageHeader diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index e3e50ca24..4928b6745 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -9,7 +9,6 @@ import { import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {useQueryClient} from '@tanstack/react-query' - import {track} from '#/lib/analytics/analytics' import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' @@ -27,6 +26,8 @@ import {Haptics} from 'lib/haptics' import {TextLink} from 'view/com/util/Link' import {logger} from '#/logger' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import { usePreferencesQuery, usePinFeedMutation, @@ -52,6 +53,7 @@ const HITSLOP_BOTTOM = { type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'> export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) { const pal = usePalette('default') + const {_} = useLingui() const {isMobile, isTabletOrDesktop} = useWebMediaQueries() const {screen} = useAnalytics() const setMinimalShellMode = useSetMinimalShellMode() @@ -71,11 +73,11 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) { pal.border, isTabletOrDesktop && styles.desktopContainer, ]}> - <ViewHeader title="Edit My Feeds" showOnDesktop showBorder /> + <ViewHeader title={_(msg`Edit My Feeds`)} showOnDesktop showBorder /> <ScrollView style={s.flex1}> <View style={[pal.text, pal.border, styles.title]}> <Text type="title" style={pal.text}> - Pinned Feeds + <Trans>Pinned Feeds</Trans> </Text> </View> {preferences?.feeds ? ( @@ -88,7 +90,7 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) { styles.empty, ]}> <Text type="lg" style={[pal.text]}> - You don't have any pinned feeds. + <Trans>You don't have any pinned feeds.</Trans> </Text> </View> ) : ( @@ -101,7 +103,7 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) { )} <View style={[pal.text, pal.border, styles.title]}> <Text type="title" style={pal.text}> - Saved Feeds + <Trans>Saved Feeds</Trans> </Text> </View> {preferences?.feeds ? ( @@ -114,7 +116,7 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) { styles.empty, ]}> <Text type="lg" style={[pal.text]}> - You don't have any saved feeds. + <Trans>You don't have any saved feeds.</Trans> </Text> </View> ) : ( @@ -128,15 +130,17 @@ export const SavedFeeds = withAuthRequired(function SavedFeedsImpl({}: Props) { <View style={styles.footerText}> <Text type="sm" style={pal.textLight}> - Feeds are custom algorithms that users build with a little coding - expertise.{' '} - <TextLink - type="sm" - style={pal.link} - href="https://github.com/bluesky-social/feed-generator" - text="See this guide" - />{' '} - for more information. + <Trans> + Feeds are custom algorithms that users build with a little coding + expertise.{' '} + <TextLink + type="sm" + style={pal.link} + href="https://github.com/bluesky-social/feed-generator" + text="See this guide" + />{' '} + for more information. + </Trans> </Text> </View> <View style={{height: 100}} /> diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index a17c0d407..0788dd79d 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -222,10 +222,10 @@ function SearchScreenPostResults({query}: {query: string}) { return results?.pages.flatMap(page => page.posts) || [] }, [results]) const items = React.useMemo(() => { - let items: SearchResultSlice[] = [] + let temp: SearchResultSlice[] = [] for (const post of posts) { - items.push({ + temp.push({ type: 'post', key: post.uri, post, @@ -233,13 +233,13 @@ function SearchScreenPostResults({query}: {query: string}) { } if (isFetchingNextPage) { - items.push({ + temp.push({ type: 'loadingMore', key: 'loadingMore', }) } - return items + return temp }, [posts, isFetchingNextPage]) return error ? ( @@ -299,9 +299,9 @@ function SearchScreenUserResults({query}: {query: string}) { React.useEffect(() => { async function getResults() { - const results = await search({query, limit: 30}) + const searchResults = await search({query, limit: 30}) - if (results) { + if (searchResults) { setDataUpdatedAt(Date.now()) setResults(results) setIsFetched(true) @@ -314,7 +314,7 @@ function SearchScreenUserResults({query}: {query: string}) { setResults([]) setIsFetched(false) } - }, [query, setDataUpdatedAt, search]) + }, [query, setDataUpdatedAt, search, results]) return isFetched ? ( <> diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index 6c61a699b..1f7623440 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -268,7 +268,7 @@ export const SettingsScreen = withAuthRequired(function Settings({}: Props) { return ( <View style={[s.hContentRegion]} testID="settingsScreen"> - <ViewHeader title="Settings" /> + <ViewHeader title={_(msg`Settings`)} /> <ScrollView style={[s.hContentRegion]} contentContainerStyle={isMobile && pal.viewLight} @@ -281,7 +281,7 @@ export const SettingsScreen = withAuthRequired(function Settings({}: Props) { </Text> <View style={[styles.infoLine]}> <Text type="lg-medium" style={pal.text}> - Email:{' '} + <Trans>Email:</Trans>{' '} </Text> {currentAccount.emailConfirmed && ( <> diff --git a/src/view/screens/Support.tsx b/src/view/screens/Support.tsx index 7106b4136..6856f6759 100644 --- a/src/view/screens/Support.tsx +++ b/src/view/screens/Support.tsx @@ -10,11 +10,14 @@ import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {HELP_DESK_URL} from 'lib/constants' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'Support'> export const SupportScreen = (_props: Props) => { const pal = usePalette('default') const setMinimalShellMode = useSetMinimalShellMode() + const {_} = useLingui() useFocusEffect( React.useCallback(() => { @@ -24,19 +27,21 @@ export const SupportScreen = (_props: Props) => { return ( <View> - <ViewHeader title="Support" /> + <ViewHeader title={_(msg`Support`)} /> <CenteredView> <Text type="title-xl" style={[pal.text, s.p20, s.pb5]}> - Support + <Trans>Support</Trans> </Text> <Text style={[pal.text, s.p20]}> - The support form has been moved. If you need help, please - <TextLink - href={HELP_DESK_URL} - text=" click here" - style={pal.link} - />{' '} - or visit {HELP_DESK_URL} to get in touch with us. + <Trans> + The support form has been moved. If you need help, please + <TextLink + href={HELP_DESK_URL} + text=" click here" + style={pal.link} + />{' '} + or visit {HELP_DESK_URL} to get in touch with us. + </Trans> </Text> </CenteredView> </View> diff --git a/src/view/screens/TermsOfService.tsx b/src/view/screens/TermsOfService.tsx index b7a388b65..c20890e29 100644 --- a/src/view/screens/TermsOfService.tsx +++ b/src/view/screens/TermsOfService.tsx @@ -9,11 +9,14 @@ import {ScrollView} from 'view/com/util/Views' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {useSetMinimalShellMode} from '#/state/shell' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'> export const TermsOfServiceScreen = (_props: Props) => { const pal = usePalette('default') const setMinimalShellMode = useSetMinimalShellMode() + const {_} = useLingui() useFocusEffect( React.useCallback(() => { @@ -23,11 +26,11 @@ export const TermsOfServiceScreen = (_props: Props) => { return ( <View> - <ViewHeader title="Terms of Service" /> + <ViewHeader title={_(msg`Terms of Service`)} /> <ScrollView style={[s.hContentRegion, pal.view]}> <View style={[s.p20]}> <Text style={pal.text}> - The Terms of Service have been moved to{' '} + <Trans>The Terms of Service have been moved to</Trans>{' '} <TextLink style={pal.link} href="https://blueskyweb.xyz/support/tos" diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index af4da668d..3d84c61bb 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -247,7 +247,7 @@ export function DrawerContent() { /> ) } - label="Search" + label={_(msg`Search`)} accessibilityLabel={_(msg`Search`)} accessibilityHint="" bold={isAtSearch} @@ -269,7 +269,7 @@ export function DrawerContent() { /> ) } - label="Home" + label={_(msg`Home`)} accessibilityLabel={_(msg`Home`)} accessibilityHint="" bold={isAtHome} @@ -291,7 +291,7 @@ export function DrawerContent() { /> ) } - label="Notifications" + label={_(msg`Notifications`)} accessibilityLabel={_(msg`Notifications`)} accessibilityHint={ numUnreadNotifications === '' @@ -318,7 +318,7 @@ export function DrawerContent() { /> ) } - label="Feeds" + label={_(msg`Feeds`)} accessibilityLabel={_(msg`Feeds`)} accessibilityHint="" bold={isAtFeeds} @@ -326,14 +326,14 @@ export function DrawerContent() { /> <MenuItem icon={<ListIcon strokeWidth={2} style={pal.text} size={26} />} - label="Lists" + label={_(msg`Lists`)} accessibilityLabel={_(msg`Lists`)} accessibilityHint="" onPress={onPressLists} /> <MenuItem icon={<HandIcon strokeWidth={5} style={pal.text} size={24} />} - label="Moderation" + label={_(msg`Moderation`)} accessibilityLabel={_(msg`Moderation`)} accessibilityHint="" onPress={onPressModeration} @@ -354,7 +354,7 @@ export function DrawerContent() { /> ) } - label="Profile" + label={_(msg`Profile`)} accessibilityLabel={_(msg`Profile`)} accessibilityHint="" onPress={onPressProfile} @@ -367,7 +367,7 @@ export function DrawerContent() { strokeWidth={1.75} /> } - label="Settings" + label={_(msg`Settings`)} accessibilityLabel={_(msg`Settings`)} accessibilityHint="" onPress={onPressSettings} diff --git a/src/view/shell/desktop/Feeds.tsx b/src/view/shell/desktop/Feeds.tsx index dc5e311f4..eeeca4fd8 100644 --- a/src/view/shell/desktop/Feeds.tsx +++ b/src/view/shell/desktop/Feeds.tsx @@ -4,10 +4,13 @@ import {useNavigationState} from '@react-navigation/native' import {usePalette} from 'lib/hooks/usePalette' import {TextLink} from 'view/com/util/Link' import {getCurrentRoute} from 'lib/routes/helpers' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {usePinnedFeedsInfos} from '#/state/queries/feed' export function DesktopFeeds() { const pal = usePalette('default') + const {_} = useLingui() const feeds = usePinnedFeedsInfos() const route = useNavigationState(state => { @@ -47,7 +50,7 @@ export function DesktopFeeds() { <TextLink type="lg" href="/feeds" - text="More feeds" + text={_(msg`More feeds`)} style={[pal.link]} /> </View> diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 8f6998abf..bb76ff183 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -52,6 +52,7 @@ function ProfileCard() { const {currentAccount} = useSession() const {isLoading, data: profile} = useProfileQuery({did: currentAccount!.did}) const {isDesktop} = useWebMediaQueries() + const {_} = useLingui() const size = 48 return !isLoading && profile ? ( @@ -61,7 +62,7 @@ function ProfileCard() { handle: currentAccount!.handle, })} style={[styles.profileCard, !isDesktop && styles.profileCardTablet]} - title="My Profile" + title={_(msg`My Profile`)} asAnchor> <UserAvatar avatar={profile.avatar} size={size} /> </Link> @@ -269,6 +270,7 @@ function ComposeBtn() { export function DesktopLeftNav() { const {currentAccount} = useSession() const pal = usePalette('default') + const {_} = useLingui() const {isDesktop, isTablet} = useWebMediaQueries() const numUnread = useUnreadNotifications() @@ -292,7 +294,7 @@ export function DesktopLeftNav() { style={pal.text} /> } - label="Home" + label={_(msg`Home`)} /> <NavItem href="/search" @@ -310,7 +312,7 @@ export function DesktopLeftNav() { style={pal.text} /> } - label="Search" + label={_(msg`Search`)} /> <NavItem href="/feeds" @@ -328,7 +330,7 @@ export function DesktopLeftNav() { size={isDesktop ? 24 : 28} /> } - label="Feeds" + label={_(msg`Feeds`)} /> <NavItem href="/notifications" @@ -347,7 +349,7 @@ export function DesktopLeftNav() { style={pal.text} /> } - label="Notifications" + label={_(msg`Notifications`)} /> <NavItem href="/lists" @@ -365,7 +367,7 @@ export function DesktopLeftNav() { strokeWidth={3} /> } - label="Lists" + label={_(msg`Lists`)} /> <NavItem href="/moderation" @@ -383,7 +385,7 @@ export function DesktopLeftNav() { size={isDesktop ? 20 : 26} /> } - label="Moderation" + label={_(msg`Moderation`)} /> <NavItem href={currentAccount ? makeProfileLink(currentAccount) : '/'} @@ -419,7 +421,7 @@ export function DesktopLeftNav() { style={pal.text} /> } - label="Settings" + label={_(msg`Settings`)} /> <ComposeBtn /> </View> diff --git a/src/view/shell/desktop/RightNav.tsx b/src/view/shell/desktop/RightNav.tsx index 3b94c12ef..51ee28418 100644 --- a/src/view/shell/desktop/RightNav.tsx +++ b/src/view/shell/desktop/RightNav.tsx @@ -12,12 +12,15 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {pluralize} from 'lib/strings/helpers' import {formatCount} from 'view/com/util/numeric/format' import {useModalControls} from '#/state/modals' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useSession} from '#/state/session' import {useInviteCodesQuery} from '#/state/queries/invites' export function DesktopRightNav() { const pal = usePalette('default') const palError = usePalette('error') + const {_} = useLingui() const {isSandbox, hasSession, currentAccount} = useSession() const {isTablet} = useWebMediaQueries() @@ -45,7 +48,7 @@ export function DesktopRightNav() { email: currentAccount!.email, handle: currentAccount!.handle, })} - text="Send feedback" + text={_(msg`Feedback`)} /> <Text type="md" style={pal.textLight}> · @@ -54,7 +57,7 @@ export function DesktopRightNav() { type="md" style={pal.link} href="https://blueskyweb.xyz/support/privacy-policy" - text="Privacy" + text={_(msg`Privacy`)} /> <Text type="md" style={pal.textLight}> · @@ -63,7 +66,7 @@ export function DesktopRightNav() { type="md" style={pal.link} href="https://blueskyweb.xyz/support/tos" - text="Terms" + text={_(msg`Terms`)} /> <Text type="md" style={pal.textLight}> · @@ -72,7 +75,7 @@ export function DesktopRightNav() { type="md" style={pal.link} href={HELP_DESK_URL} - text="Help" + text={_(msg`Help`)} /> </View> </View> |