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
This commit is contained in:
Ansh 2023-11-20 13:29:27 -08:00 committed by GitHub
parent 019aae5f01
commit c5b6f88e9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 5121 additions and 2058 deletions

View file

@ -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}

View file

@ -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}

View file

@ -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]}>

View file

@ -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) {

View file

@ -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

View file

@ -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>

View file

@ -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)}{' '}

View file

@ -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}>

View file

@ -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>

View file

@ -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

View file

@ -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 =

View file

@ -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')

View file

@ -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>

View file

@ -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,

View file

@ -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,
_,
],
)

View file

@ -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,
_,
],
)

View file

@ -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,
})
},

View file

@ -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'},

View file

@ -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} />

View file

@ -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} />

View file

@ -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} />

View file

@ -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

View file

@ -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]}>
Primary Language
<Trans>App Language</Trans>
</Text>
<Text style={[pal.text, s.pb10]}>
Select your preferred language for translations in your feed.
<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]}>
<Trans>Primary Language</Trans>
</Text>
<Text style={[pal.text, s.pb10]}>
<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

View file

@ -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>

View file

@ -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>

View file

@ -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>
)}

View file

@ -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>
)}

View file

@ -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"

View file

@ -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}
/>
)}

View file

@ -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>
)

View file

@ -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>
)

View file

@ -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>

View file

@ -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={
!!(

View file

@ -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,

View file

@ -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} />

View file

@ -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(() => {

View file

@ -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>
)

View file

@ -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>
)

View file

@ -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>
)

View file

@ -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

View file

@ -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}} />

View file

@ -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 ? (
<>

View file

@ -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 && (
<>

View file

@ -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>

View file

@ -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"

View file

@ -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}

View file

@ -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>

View file

@ -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>

View file

@ -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}>
&nbsp;&middot;&nbsp;
@ -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}>
&nbsp;&middot;&nbsp;
@ -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}>
&nbsp;&middot;&nbsp;
@ -72,7 +75,7 @@ export function DesktopRightNav() {
type="md"
style={pal.link}
href={HELP_DESK_URL}
text="Help"
text={_(msg`Help`)}
/>
</View>
</View>