about summary refs log tree commit diff
path: root/src/view
diff options
context:
space:
mode:
authorAnsh <anshnanda10@gmail.com>2023-11-20 13:29:27 -0800
committerGitHub <noreply@github.com>2023-11-20 13:29:27 -0800
commitc5b6f88e9a694d79126af4f742e66833dfd528bd (patch)
tree0bfdc49ace558adc3d9d5a76fc4726f16f853d4e /src/view
parent019aae5f01cb7b503d242917ae0092c2818f3b71 (diff)
downloadvoidsky-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')
-rw-r--r--src/view/com/auth/onboarding/RecommendedFeeds.tsx79
-rw-r--r--src/view/com/auth/onboarding/RecommendedFollows.tsx71
-rw-r--r--src/view/com/auth/onboarding/WelcomeMobile.tsx8
-rw-r--r--src/view/com/composer/Composer.tsx8
-rw-r--r--src/view/com/feeds/FeedSourceCard.tsx9
-rw-r--r--src/view/com/modals/SwitchAccount.tsx2
-rw-r--r--src/view/com/notifications/FeedItem.tsx3
-rw-r--r--src/view/com/post-thread/PostThread.tsx2
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx6
-rw-r--r--src/view/com/posts/FeedErrorMessage.tsx9
-rw-r--r--src/view/com/profile/ProfileHeader.tsx33
-rw-r--r--src/view/com/util/AccountDropdownBtn.tsx2
-rw-r--r--src/view/com/util/ErrorBoundary.tsx5
-rw-r--r--src/view/com/util/TimeElapsed.tsx1
-rw-r--r--src/view/com/util/UserAvatar.tsx7
-rw-r--r--src/view/com/util/UserBanner.tsx7
-rw-r--r--src/view/com/util/forms/PostDropdownBtn.tsx19
-rw-r--r--src/view/com/util/post-ctrls/RepostButton.web.tsx4
-rw-r--r--src/view/screens/AppPasswords.tsx31
-rw-r--r--src/view/screens/CommunityGuidelines.tsx19
-rw-r--r--src/view/screens/CopyrightPolicy.tsx19
-rw-r--r--src/view/screens/Feeds.tsx2
-rw-r--r--src/view/screens/LanguageSettings.tsx125
-rw-r--r--src/view/screens/Lists.tsx7
-rw-r--r--src/view/screens/Moderation.tsx13
-rw-r--r--src/view/screens/ModerationBlockedAccounts.tsx21
-rw-r--r--src/view/screens/ModerationMutedAccounts.tsx19
-rw-r--r--src/view/screens/NotFound.tsx11
-rw-r--r--src/view/screens/Notifications.tsx9
-rw-r--r--src/view/screens/PostLikedBy.tsx5
-rw-r--r--src/view/screens/PostRepostedBy.tsx5
-rw-r--r--src/view/screens/PostThread.tsx5
-rw-r--r--src/view/screens/PreferencesHomeFeed.tsx16
-rw-r--r--src/view/screens/PreferencesThreads.tsx2
-rw-r--r--src/view/screens/PrivacyPolicy.tsx19
-rw-r--r--src/view/screens/ProfileFeed.tsx7
-rw-r--r--src/view/screens/ProfileFeedLikedBy.tsx5
-rw-r--r--src/view/screens/ProfileFollowers.tsx5
-rw-r--r--src/view/screens/ProfileFollows.tsx5
-rw-r--r--src/view/screens/ProfileList.tsx40
-rw-r--r--src/view/screens/SavedFeeds.tsx34
-rw-r--r--src/view/screens/Search/Search.tsx14
-rw-r--r--src/view/screens/Settings.tsx4
-rw-r--r--src/view/screens/Support.tsx23
-rw-r--r--src/view/screens/TermsOfService.tsx7
-rw-r--r--src/view/shell/Drawer.tsx16
-rw-r--r--src/view/shell/desktop/Feeds.tsx5
-rw-r--r--src/view/shell/desktop/LeftNav.tsx18
-rw-r--r--src/view/shell/desktop/RightNav.tsx11
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}>
             &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>