diff options
108 files changed, 10334 insertions, 1365 deletions
diff --git a/babel.config.js b/babel.config.js index 924120495..825916676 100644 --- a/babel.config.js +++ b/babel.config.js @@ -45,6 +45,7 @@ module.exports = function (api) { }, }, ], + 'macros', 'react-native-reanimated/plugin', // NOTE: this plugin MUST be last ], env: { diff --git a/docs/internationalization.md b/docs/internationalization.md new file mode 100644 index 000000000..3c1af7a4d --- /dev/null +++ b/docs/internationalization.md @@ -0,0 +1,113 @@ +# Internationalization + +We want the official Bluesky app to be supported in as many languages as possible. If you want to help us translate the app, please open a PR or issue on the [Bluesky app repo on GitHub](https://github.com/bluesky-social/social-app) + +## Tools +We are using Lingui to manage translations. You can find the documentation [here](https://lingui.dev/). + +### Adding new strings +When adding a new string, do it as follows: +```jsx +// Before +import { Text } from "react-native"; + +<Text>Hello World</Text> +``` + +```jsx +// After +import { Text } from "react-native"; +import { Trans } from "@lingui/macro"; + +<Text><Trans>Hello World</Trans></Text> +``` + +The `<Trans>` macro will extract the string and add it to the catalog. It is not really a component, but a macro. Further reading [here](https://lingui.dev/ref/macro.html) + +However sometimes you will run into this case: +```jsx +// Before +import { Text } from "react-native"; + +const text = "Hello World"; +<Text accessibilityLabel="Label is here">{text}</Text> +``` +In this case, you cannot use the `useLingui()` hook: +```jsx +import { msg } from "@lingui/macro"; +import { useLingui } from "@lingui/react"; + +const { _ } = useLingui(); +return <Text accessibilityLabel={_(msg`Label is here`)}>{text}</Text> +``` + +If you want to do this outside of a React component, you can use the `t` macro instead (note: this won't react to changes if the locale is switched dynamically within the app): +```jsx +import { t } from "@lingui/macro"; + +const text = t`Hello World`; +``` + +We can then run `yarn intl:extract` to update the catalog in `src/locale/locales/{locale}/messages.po`. This will add the new string to the catalog. +We can then run `yarn intl:compile` to update the translation files in `src/locale/locales/{locale}/messages.js`. This will add the new string to the translation files. +The configuration for translations is defined in `lingui.config.js` + +So the workflow is as follows: +1. Wrap messages in Trans macro +2. Run `yarn intl:extract` command to generate message catalogs +3. Translate message catalogs (send them to translators usually) +4. Run `yarn intl:compile` to create runtime catalogs +5. Load runtime catalog +6. Enjoy translated app! + +### Common pitfalls +These pitfalls are memoization pitfalls that will cause the components to not re-render when the locale is changed -- causing stale translations to be shown. + +```jsx +import { msg } from "@lingui/macro"; +import { i18n } from "@lingui/core"; + +const welcomeMessage = msg`Welcome!`; + +// ❌ Bad! This code won't work +export function Welcome() { + const buggyWelcome = useMemo(() => { + return i18n._(welcomeMessage); + }, []); + + return <div>{buggyWelcome}</div>; +} + +// ❌ Bad! This code won't work either because the reference to i18n does not change +export function Welcome() { + const { i18n } = useLingui(); + + const buggyWelcome = useMemo(() => { + return i18n._(welcomeMessage); + }, [i18n]); + + return <div>{buggyWelcome}</div>; +} + +// ✅ Good! `useMemo` has i18n context in the dependency +export function Welcome() { + const linguiCtx = useLingui(); + + const welcome = useMemo(() => { + return linguiCtx.i18n._(welcomeMessage); + }, [linguiCtx]); + + return <div>{welcome}</div>; +} + +// 🤩 Better! `useMemo` consumes the `_` function from the Lingui context +export function Welcome() { + const { _ } = useLingui(); + + const welcome = useMemo(() => { + return _(welcomeMessage); + }, [_]); + + return <div>{welcome}</div>; +} +``` \ No newline at end of file diff --git a/lingui.config.js b/lingui.config.js new file mode 100644 index 000000000..a4219a1bd --- /dev/null +++ b/lingui.config.js @@ -0,0 +1,11 @@ +/** @type {import('@lingui/conf').LinguiConfig} */ +module.exports = { + locales: ['en', 'cs', 'fr', 'hi', 'es'], + catalogs: [ + { + path: '<rootDir>/src/locale/locales/{locale}/messages', + include: ['src'], + }, + ], + format: 'po', +} diff --git a/package.json b/package.json index efb0942df..585e1e23e 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,9 @@ "perf:test:measure": "NODE_ENV=test flashlight test --bundleId xyz.blueskyweb.app --testCommand 'yarn perf:test' --duration 150000 --resultsFilePath .perf/results.json", "perf:test:results": "NODE_ENV=test flashlight report .perf/results.json", "perf:measure": "NODE_ENV=test flashlight measure", - "build:apk": "eas build -p android --profile dev-android-apk" + "build:apk": "eas build -p android --profile dev-android-apk", + "intl:extract": "lingui extract", + "intl:compile": "lingui compile" }, "dependencies": { "@atproto/api": "^0.6.23", @@ -42,6 +44,7 @@ "@fortawesome/free-solid-svg-icons": "^6.1.1", "@fortawesome/react-native-fontawesome": "^0.3.0", "@gorhom/bottom-sheet": "^4.5.1", + "@lingui/react": "^4.5.0", "@mattermost/react-native-paste-input": "^0.6.4", "@miblanchard/react-native-slider": "^2.3.1", "@react-native-async-storage/async-storage": "1.18.2", @@ -164,10 +167,12 @@ }, "devDependencies": { "@atproto/dev-env": "^0.2.5", - "@babel/core": "^7.20.0", + "@babel/core": "^7.23.2", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", "@did-plc/server": "^0.0.1", + "@lingui/cli": "^4.5.0", + "@lingui/macro": "^4.5.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@react-native-community/eslint-config": "^3.0.0", "@testing-library/jest-native": "^5.4.1", @@ -192,6 +197,7 @@ "@typescript-eslint/parser": "^5.48.2", "babel-jest": "^29.4.2", "babel-loader": "^9.1.2", + "babel-plugin-macros": "^3.1.0", "babel-plugin-module-resolver": "^5.0.0", "babel-plugin-react-native-web": "^0.18.12", "detox": "^20.13.0", diff --git a/src/App.web.tsx b/src/App.web.tsx index 9e5b99a9f..ef275b392 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -16,6 +16,9 @@ import {Shell} from 'view/shell/index' import {ToastContainer} from 'view/com/util/Toast.web' import {ThemeProvider} from 'lib/ThemeContext' import {queryClient} from 'lib/react-query' +import {i18n} from '@lingui/core' +import {I18nProvider} from '@lingui/react' +import {defaultLocale, dynamicActivate} from './locale/i18n' import {Provider as ShellStateProvider} from 'state/shell' import {Provider as ModalStateProvider} from 'state/modals' import {Provider as MutedThreadsProvider} from 'state/muted-threads' @@ -34,6 +37,7 @@ const InnerApp = observer(function AppImpl() { setRootStore(store) analytics.init(store) }) + dynamicActivate(defaultLocale) // async import of locale data }, []) // show nothing prior to init @@ -47,9 +51,11 @@ const InnerApp = observer(function AppImpl() { <RootSiblingParent> <analytics.Provider> <RootStoreProvider value={rootStore}> - <SafeAreaProvider> - <Shell /> - </SafeAreaProvider> + <I18nProvider i18n={i18n}> + <SafeAreaProvider> + <Shell /> + </SafeAreaProvider> + </I18nProvider> <ToastContainer /> </RootStoreProvider> </analytics.Provider> diff --git a/src/locale/i18n.ts b/src/locale/i18n.ts new file mode 100644 index 000000000..9c6a017ff --- /dev/null +++ b/src/locale/i18n.ts @@ -0,0 +1,20 @@ +import {i18n} from '@lingui/core' + +export const locales = { + en: 'English', + cs: 'Česky', + fr: 'Français', + hi: 'हिंदी', + es: 'Español', +} +export const defaultLocale = 'en' + +/** + * We do a dynamic import of just the catalog that we need + * @param locale any locale string + */ +export async function dynamicActivate(locale: string) { + const {messages} = await import(`./locales/${locale}/messages`) + i18n.load(locale, messages) + i18n.activate(locale) +} diff --git a/src/locale/locales/cs/messages.js b/src/locale/locales/cs/messages.js new file mode 100644 index 000000000..8d85ab77c --- /dev/null +++ b/src/locale/locales/cs/messages.js @@ -0,0 +1 @@ +/*eslint-disable*/module.exports={messages:JSON.parse("{\"PBodTo\":\"- end of feed -\",\"EtUMsZ\":\". This warning is only available for posts with media attached.\",\"J/hVSQ\":[[\"0\"]],\"hZQerY\":[[\"0\"],\" \",[\"purposeLabel\"],\" List\"],\"6RmyWt\":\"<0>Here is your app password.</0> Use this to sign into the other app along with your handle.\",\"AnNF5e\":\"Accessibility\",\"AeXO77\":\"Account\",\"4WY4MD\":\"Account options\",\"m16xKo\":\"Add\",\"fBBX+K\":\"Add a content warning\",\"JU3hs2\":\"Add a user to this list\",\"MPPZ54\":\"Add account\",\"LkA8jz\":\"Add alt text\",\"Z8idyM\":\"Add details\",\"AoXl11\":\"Add details to report\",\"iE6B/9\":\"Add link card\",\"EXHdP1\":\"Add link card:\",\"x6laaL\":\"Add the following DNS record to your domain:\",\"jRrQFe\":\"Adjust the number of likes a reply must have to be shown in your feed.\",\"qLa52r\":\"Adult Content\",\"sxkWRg\":\"Advanced\",\"u2HO/d\":\"ALT\",\"u/DP73\":\"Alt text\",\"0QlT7/\":\"Alt text describes images for blind and low-vision users, and helps give context to everyone.\",\"woXbjq\":[\"An email has been sent to \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"Fon2dK\":[\"An email has been sent to your previous address, \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"HZFm5R\":\"and\",\"SMmUnj\":\"App passwords\",\"aAIQg2\":\"Appearance\",\"EbvWd3\":\"Artistic or non-erotic nudity.\",\"iH8pgl\":\"Back\",\"ehOkF+\":\"Basics\",\"+gCI2a\":\"Birthday\",\"pieVBA\":\"Birthday:\",\"HFCE4A\":\"Blocked post.\",\"7A9u1j\":\"Bluesky\",\"ZHmKSm\":\"Bluesky is flexible.\",\"odLrdl\":\"Bluesky is open.\",\"/LsWK4\":\"Bluesky is public.\",\"C50OGr\":\"Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon.\",\"klVoaP\":\"Bluesky.Social\",\"qxBitM\":[\"Build version \",[\"0\"],\" \",[\"1\"]],\"rT2cV+\":\"Camera\",\"JGGrPC\":\"Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long.\",\"dEgA5A\":\"Cancel\",\"aMH9rr\":\"Cancel account deletion\",\"kc3E4R\":\"Cancel add image alt text\",\"wg4LHQ\":\"Cancel change handle\",\"hFL1Li\":\"Cancel image crop\",\"tijH8t\":\"Cancel profile editing\",\"Qe4C/d\":\"Cancel quote post\",\"5TviPn\":\"Cancel search\",\"nss3UV\":\"Cancel waitlist signup\",\"o+XJ9D\":\"Change\",\"pQco5R\":\"Change handle\",\"Q5e1U/\":\"Change Handle\",\"a3NAfL\":\"Change my email\",\"4vatyk\":\"Change Your Email\",\"/+X+/K\":\"Check your inbox for an email with the confirmation code to enter below:\",\"Rt502e\":\"Choose Service\",\"/L45sc\":\"Choose the algorithms that power your experience with custom feeds.\",\"Wk8hkn\":\"Choose your password\",\"QbvBNV\":\"Clear search query\",\"flH7u/\":\"Close alert\",\"hYmnbk\":\"Close bottom drawer\",\"47L1V1\":\"Close image\",\"l49ujN\":\"Close image viewer\",\"UryHFO\":\"Close navigation footer\",\"o8UUti\":\"Compose reply\",\"7VpPHA\":\"Confirm\",\"q8upsf\":\"Confirm Change\",\"8pNKIr\":\"Confirm content language settings\",\"tGg8Kt\":\"Confirm delete account\",\"ioZOzk\":\"Confirmation code\",\"J28zul\":\"Connecting...\",\"m8j6up\":\"Content Filtering\",\"/PlAsF\":\"Content Languages\",\"cogwXi\":\"Content Warning\",\"FG7AQv\":\"Content warnings\",\"6V3Ea3\":\"Copied\",\"he3ygx\":\"Copy\",\"7wWvgo\":\"Could not load feed\",\"8NNr/O\":\"Could not load list\",\"mpt9T+\":\"Create a new account\",\"IS0nrP\":\"Create Account\",\"6HbhpU\":\"Create new account\",\"GAD3Dx\":\"Custom domain\",\"ZQKLI1\":\"Danger Zone\",\"ZDGm40\":\"Delete account\",\"vzX5FB\":\"Delete Account\",\"gUEtxf\":\"Delete app password\",\"ktknoE\":\"Delete my account\",\"szz0+N\":\"Delete my account…\",\"u+1OHY\":\"Deleted post.\",\"Nu4oKW\":\"Description\",\"dacKHE\":\"Dev Server\",\"2ygkE8\":\"Developer Tools\",\"pbLwal\":\"Discover new feeds\",\"pfa8F0\":\"Display name\",\"0gS7M5\":\"Display Name\",\"iZ5pMB\":\"Domain verified!\",\"DPfwMq\":\"Done\",\"zT97vP\":[\"Done\",[\"extraText\"]],\"XQFMOm\":\"Edit image\",\"cLmurE\":\"Edit My Feeds\",\"bRZ5XW\":\"Edit my profile\",\"9OpVZg\":\"Edit profile\",\"QJQd1J\":\"Edit Profile\",\"Jn7kox\":\"Edit Saved Feeds\",\"O3oNi5\":\"Email\",\"ATGYL1\":\"Email address\",\"pJJ0Vp\":\"Email Updated\",\"96mted\":\"Enable this setting to only see replies between people you follow.\",\"YbIxza\":\"Enter the address of your provider:\",\"BfIgP6\":\"Enter the domain you want to use\",\"xRPn3U\":\"Enter your email address\",\"+inPGm\":\"Enter your new email address below.\",\"T0KLp4\":\"Enter your username and password\",\"0PkE20\":\"Expand alt text\",\"4yCy8i\":\"Feed offline\",\"N0CqyO\":\"Feed Preferences\",\"YirHq7\":\"Feedback\",\"2DoBvq\":\"Feeds\",\"Qzj1WT\":\"Finding similar accounts...\",\"QKSrQV\":\"Fine-tune the content you see on your home screen.\",\"r+KeyR\":\"Fine-tune the discussion threads.\",\"MKEPCY\":\"Follow\",\"NIjL2Y\":\"following\",\"y6sq5j\":\"Following\",\"p3UO/y\":\"Follows you\",\"5RhDkD\":\"For security reasons, we'll need to send a confirmation code to your email address.\",\"NJPhAO\":\"For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one.\",\"5bDfuq\":\"Forgot\",\"hEPLrs\":\"Forgot password\",\"dn8X5t\":\"Forgot Password\",\"U+kFad\":\"Gallery\",\"c3b0B0\":\"Get Started\",\"CKyk7Q\":\"Go back\",\"sr0UJD\":\"Go Back\",\"Rtp0y7\":\"Go to next\",\"Nf7oXL\":\"Handle\",\"c3XJ18\":\"Help\",\"vLyv1R\":\"Hide\",\"qdOx2q\":\"Hide user list\",\"i0qMbr\":\"Home\",\"sXZ8IU\":\"Home Feed Preferences\",\"yt7fhu\":\"Hosting provider\",\"s2xA6t\":\"Hosting provider address\",\"o+axy6\":\"I have a code\",\"wey2os\":\"I have my own domain\",\"WlEcKr\":\"If none are selected, suitable for all ages.\",\"VCk0rR\":\"Image alt text\",\"STGpNQ\":\"Image options\",\"dSKHAa\":\"Invalid username or password\",\"MFKlMB\":\"Invite\",\"F5MZVk\":\"Invite a Friend\",\"6KlkHI\":\"Invite code\",\"F75w8j\":\"Join the waitlist\",\"6iVTdm\":\"Join the waitlist.\",\"SNzppu\":\"Join Waitlist\",\"Dcq5kL\":\"Language selection\",\"GAmD3h\":\"Languages\",\"NgeSlx\":\"Learn More\",\"rj0Lke\":\"Learn more about this warning\",\"kq2ga7\":\"Leave them all unchecked to see any language.\",\"QfDITI\":\"Leaving Bluesky\",\"Esfg1M\":\"Let's get your password reset!\",\"BvSY1i\":\"Like this feed\",\"FuZWua\":\"List Avatar\",\"8mjA4F\":\"List Name\",\"h16FyT\":\"Lists\",\"ujW4FW\":\"Load more posts\",\"VkLESX\":\"Load new posts\",\"jl0AFf\":\"Local dev server\",\"cR9UpQ\":\"Login to account that is not listed\",\"2U/gDT\":\"Make sure this is where you intend to go!\",\"zucql+\":\"Menu\",\"DzmsLV\":\"Moderation\",\"3Siwmw\":\"More options\",\"Y17r45\":\"More post options\",\"Mysqyf\":\"My Birthday\",\"6MBNS/\":\"My Feeds\",\"Ha6iBv\":\"My Saved Feeds\",\"6YtxFj\":\"Name\",\"8yolS6\":\"Never lose access to your followers and data.\",\"2B7HLH\":\"New post\",\"FGrimz\":\"New Post\",\"hXzOVo\":\"Next\",\"EatZYJ\":\"Next image\",\"flmDTf\":\"No description\",\"fOlAiK\":[\"No results found for \\\"\",[\"query\"],\"\\\"\"],\"kA9DpB\":[\"No results found for \",[\"0\"]],\"ispbnl\":\"Not Applicable\",\"iDNBZe\":\"Notifications\",\"UaXeX3\":\"Okay\",\"Cqo2D+\":\"One or more images is missing alt text.\",\"M/Q2aG\":\"Open navigation\",\"M5PuNq\":\"Opens configurable language settings\",\"eSqpax\":\"Opens modal for using custom domain\",\"vYwHHI\":\"Opens moderation settings\",\"0tHyB7\":\"Opens screen with all saved feeds\",\"nmRoY/\":\"Opens the app password settings page\",\"6e9Apv\":\"Opens the home feed preferences\",\"O87Dr/\":\"Opens the storybook page\",\"G+PVmg\":\"Opens the system log page\",\"Jqb7sy\":\"Opens the threads preferences\",\"b22AVl\":\"Other account\",\"n+HLOP\":\"Other service\",\"1PKxQ7\":\"Other...\",\"8ZsakT\":\"Password\",\"DKeVgZ\":\"Password updated!\",\"VeZE5Q\":\"Pictures meant for adults.\",\"Apyknf\":\"Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed.\",\"9qpQ5O\":\"Please enter a unique name for this App Password or use our randomly generated one.\",\"QJr5Xp\":\"Please enter your password as well:\",\"y28hnO\":\"Post\",\"h5RcXU\":\"Post hidden\",\"r5zLS0\":\"Post language\",\"AzCucI\":\"Post Languages\",\"tJFPmV\":\"Post not found\",\"0+DQbr\":\"Potentially Misleading Link\",\"MHk+7g\":\"Previous image\",\"x8iR7V\":\"Prioritize Your Follows\",\"vERlcd\":\"Profile\",\"MrgqOW\":\"Protect your account by verifying your email.\",\"8HFFRQ\":\"Quote post\",\"+KrAHa\":\"Quote Post\",\"WlWsdE\":\"Ratios\",\"t/YqKh\":\"Remove\",\"5ywtDz\":\"Remove image\",\"Dw/XUh\":\"Remove image preview\",\"Veu9gK\":\"Reply Filters\",\"lQWbAr\":[\"Report \",[\"collectionName\"]],\"6IcSvC\":\"Report post\",\"JOV5dR\":\"Repost or quote post\",\"bqG37Z\":\"Request Change\",\"8XIT+P\":\"Required for this provider\",\"vJgYMA\":\"Reset code\",\"xEL92I\":\"Reset onboarding state\",\"bee/Fw\":\"Reset preferences state\",\"wToeoz\":\"Resets the onboarding state\",\"nIU7qI\":\"Resets the preferences state\",\"6gRgw8\":\"Retry\",\"hAbYQa\":\"Retry change handle\",\"tfDRzk\":\"Save\",\"KV2YQQ\":\"Save alt text\",\"y3aU20\":\"Save changes\",\"IUwGEM\":\"Save Changes\",\"Xs07Tg\":\"Save handle change\",\"BckA7m\":\"Save image crop\",\"A1taO8\":\"Search\",\"CKROFy\":\"Security Step Required\",\"cNzyJW\":\"See what's next\",\"L5sM7N\":\"Select Bluesky Social\",\"o3dwub\":\"Select from an existing account\",\"GGw2AK\":\"Select service\",\"vp9yIB\":\"Send Confirmation Email\",\"65dxv8\":\"Send email\",\"i/TzEU\":\"Send Email\",\"RoafuO\":\"Send feedback\",\"4cijjm\":\"Send Report\",\"V/e7nf\":\"Set new password\",\"gwsie4\":\"Set this setting to \\\"No\\\" to hide all quote posts from your feed. Reposts will still be visible.\",\"IZjC3J\":\"Set this setting to \\\"No\\\" to hide all replies from your feed.\",\"KIgU3l\":\"Set this setting to \\\"No\\\" to hide all reposts from your feed.\",\"zaAyrz\":\"Set this setting to \\\"Yes\\\" to show replies in a threaded view. This is an experimental feature.\",\"fQV2eE\":\"Set this setting to \\\"Yes\\\" to show samples of your saved feeds in your following feed. This is an experimental feature.\",\"Tz0i8g\":\"Settings\",\"HfWHhJ\":\"Sexual activity or erotic nudity.\",\"8vETh9\":\"Show\",\"aWAdCb\":\"Show anyway\",\"NijgXr\":\"Show Posts from My Feeds\",\"T3Mt8m\":\"Show Quote Posts\",\"BlW8X/\":\"Show Replies\",\"X4GwDb\":\"Show replies by people you follow before all other replies.\",\"GiogzH\":\"Show Reposts\",\"fhY/fL\":\"Show users\",\"5lWFkC\":\"Sign in\",\"n1ekoW\":\"Sign In\",\"N9o7n5\":[\"Sign in as \",[\"0\"]],\"FT1MVS\":\"Sign in as...\",\"+UpfFC\":\"Sign into\",\"fcWrnU\":\"Sign out\",\"zU+Ro7\":\"Signed in as\",\"6Uau97\":\"Skip\",\"0o5BFH\":\"Sort Replies\",\"GH1Rgk\":\"Sort replies to the same post by:\",\"1DA6ap\":\"Square\",\"aKEHLj\":\"Staging\",\"tgEXwM\":\"Status page\",\"P5jja7\":\"Storybook\",\"EDl9kS\":\"Subscribe\",\"5z3ICN\":\"Subscribe to this list\",\"VjWeLI\":\"Switch Account\",\"fP8jTZ\":\"System log\",\"HF6Iah\":\"Tall\",\"p8Iz39\":\"Text input field\",\"G4EksE\":\"The post may have been deleted.\",\"KRYn8w\":[\"This \",[\"screenDescription\"],\" has been flagged:\"],\"lm845B\":\"This information is not shared with other users.\",\"5Pvw/O\":\"This is important in case you ever need to change your email or reset your password.\",\"sQQfZ9\":\"This is the service that keeps you online.\",\"CvX8qs\":\"This link is taking you to the following website:\",\"WKrUVy\":\"This post has been deleted.\",\"u9ThjD\":\"Thread Preferences\",\"zmXsk5\":\"Threaded Mode\",\"1x30Qt\":\"Toggle dropdown\",\"KFXQEt\":\"Transformations\",\"pi8x/S\":\"Translate\",\"KDw4GX\":\"Try again\",\"nc4Wfd\":\"Unable to contact your service. Please check your Internet connection.\",\"tuS5Jz\":\"Unblock\",\"vaz2uI\":[\"Update \",[\"displayName\"],\" in Lists\"],\"RXbEvi\":\"Updating...\",\"Vwkfp4\":\"Upload a text file to:\",\"jTdnU6\":\"Use app passwords to login to other Bluesky clients without giving full access to your account or password.\",\"CH1am9\":\"Use default provider\",\"t4Yp4Z\":\"User handle\",\"nZx9mr\":\"Username or email address\",\"Sxm8rQ\":\"Users\",\"MBOY4U\":\"Verify email\",\"Ejyv0o\":\"Verify my email\",\"9czCrB\":\"Verify My Email\",\"ibSVGR\":\"Verify New Email\",\"nHsQde\":\"View debug entry\",\"47jzzd\":\"View the avatar\",\"wK4H1r\":\"Visit Site\",\"qjBGxf\":\"We're so excited to have you join us!\",\"Mj7rl/\":[\"What is the issue with this \",[\"collectionName\"],\"?\"],\"3qn29J\":\"Which languages are used in this post?\",\"uawiGa\":\"Which languages would you like to see in your algorithmic feeds?\",\"I5S9ZE\":\"Wide\",\"y02THm\":\"Write post\",\"6ckZRB\":\"Write your reply\",\"STPj0e\":\"You can change hosting providers at any time.\",\"67nRLM\":\"You can now sign in with your new password.\",\"RkXibf\":\"You have blocked the author or you have been blocked by the author.\",\"tCLJ9E\":\"You have no lists.\",\"NDgp3i\":\"You have not created any app passwords yet. You can create one by pressing the button below.\",\"RrDyEb\":\"You will receive an email with a \\\"reset code.\\\" Enter that code here, then enter your new password.\",\"gdRnT7\":\"Your account\",\"k7hmsH\":\"Your birth date\",\"OubkcP\":\"Your email has been saved! We'll be in touch soon.\",\"z2L+/9\":\"Your email has been updated but not verified. As a next step, please verify your new email.\",\"XZlIVw\":\"Your email has not yet been verified. This is an important security step which we recommend.\",\"qv9f4I\":\"Your full handle will be\",\"lvcqqG\":\"Your hosting provider\",\"Oqt/PG\":\"Your posts, likes, and blocks are public. Mutes are private.\",\"MvWO9d\":\"Your user handle\"}")}; \ No newline at end of file diff --git a/src/locale/locales/cs/messages.po b/src/locale/locales/cs/messages.po new file mode 100644 index 000000000..28ad5ba4c --- /dev/null +++ b/src/locale/locales/cs/messages.po @@ -0,0 +1,1544 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-11-05 16:01-0800\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: cs\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/view/screens/Profile.tsx:212 +msgid "- end of feed -" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:138 +msgid ". This warning is only available for posts with media attached." +msgstr "" + +#: src/view/com/modals/Repost.tsx:45 +msgid "{0}" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:167 +msgid "{0} {purposeLabel} List" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:132 +msgid "<0>Here is your app password.</0> Use this to sign into the other app along with your handle." +msgstr "" + +#: src/view/com/modals/EditImage.tsx:299 +#: src/view/screens/Settings.tsx:363 +msgid "Accessibility" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:157 +#: src/view/screens/Settings.tsx:201 +msgid "Account" +msgstr "" + +#: src/view/com/util/AccountDropdownBtn.tsx:41 +msgid "Account options" +msgstr "" + +#: src/view/screens/ProfileList.tsx:710 +msgid "Add" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:57 +msgid "Add a content warning" +msgstr "" + +#: src/view/screens/ProfileList.tsx:700 +msgid "Add a user to this list" +msgstr "" + +#: src/view/screens/Settings.tsx:313 +#: src/view/screens/Settings.tsx:322 +msgid "Add account" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:119 +#: src/view/com/composer/photos/Gallery.tsx:167 +msgid "Add alt text" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:41 +#: src/view/com/modals/report/Modal.tsx:190 +msgid "Add details" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:193 +msgid "Add details to report" +msgstr "" + +#: src/view/com/composer/Composer.tsx:419 +msgid "Add link card" +msgstr "" + +#: src/view/com/composer/Composer.tsx:422 +msgid "Add link card:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:418 +msgid "Add the following DNS record to your domain:" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:140 +msgid "Adjust the number of likes a reply must have to be shown in your feed." +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:76 +msgid "Adult Content" +msgstr "" + +#: src/view/screens/Settings.tsx:493 +msgid "Advanced" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:127 +msgid "ALT" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:315 +msgid "Alt text" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:193 +msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:112 +msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:119 +msgid "An email has been sent to your previous address, {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:238 +msgid "and" +msgstr "" + +#: src/view/screens/Settings.tsx:509 +msgid "App passwords" +msgstr "" + +#: src/view/screens/Settings.tsx:378 +msgid "Appearance" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:124 +msgid "Artistic or non-erotic nudity." +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:92 +#: src/view/com/auth/login/ChooseAccountForm.tsx:111 +#: src/view/com/auth/login/LoginForm.tsx:247 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:150 +#: src/view/com/modals/report/InputIssueDetails.tsx:45 +#: src/view/com/post-thread/PostThread.tsx:317 +#: src/view/com/post-thread/PostThread.tsx:325 +#: src/view/com/post-thread/PostThread.tsx:354 +#: src/view/com/post-thread/PostThread.tsx:362 +#: src/view/com/profile/ProfileHeader.tsx:576 +msgid "Back" +msgstr "" + +#: src/view/screens/Settings.tsx:407 +msgid "Basics" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:130 +#: src/view/com/modals/BirthDateSettings.tsx:69 +msgid "Birthday" +msgstr "" + +#: src/view/screens/Settings.tsx:228 +msgid "Birthday:" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:211 +msgid "Blocked post." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:52 +#: src/view/com/auth/SplashScreen.tsx:27 +msgid "Bluesky" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:84 +msgid "Bluesky is flexible." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:73 +msgid "Bluesky is open." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:60 +msgid "Bluesky is public." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:70 +msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon." +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:78 +msgid "Bluesky.Social" +msgstr "" + +#: src/view/screens/Settings.tsx:612 +msgid "Build version {0} {1}" +msgstr "" + +#: src/view/com/composer/photos/OpenCameraBtn.tsx:62 +msgid "Camera" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:199 +msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long." +msgstr "" + +#: src/view/com/composer/Composer.tsx:271 +#: src/view/com/composer/Composer.tsx:274 +#: src/view/com/modals/AltImage.tsx:127 +#: src/view/com/modals/ChangeEmail.tsx:216 +#: src/view/com/modals/ChangeEmail.tsx:218 +#: src/view/com/modals/Confirm.tsx:89 +#: src/view/com/modals/CreateOrEditList.tsx:258 +#: src/view/com/modals/CreateOrEditList.tsx:263 +#: src/view/com/modals/DeleteAccount.tsx:142 +#: src/view/com/modals/DeleteAccount.tsx:215 +#: src/view/com/modals/EditImage.tsx:323 +#: src/view/com/modals/EditProfile.tsx:250 +#: src/view/com/modals/LinkWarning.tsx:90 +#: src/view/com/modals/Repost.tsx:74 +#: src/view/com/modals/UserAddRemoveLists.tsx:199 +#: src/view/com/modals/Waitlist.tsx:136 +#: src/view/com/search/HeaderWithInput.tsx:127 +#: src/view/shell/desktop/Search.tsx:93 +msgid "Cancel" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:138 +#: src/view/com/modals/DeleteAccount.tsx:211 +msgid "Cancel account deletion" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:122 +msgid "Cancel add image alt text" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:144 +msgid "Cancel change handle" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:134 +msgid "Cancel image crop" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:245 +msgid "Cancel profile editing" +msgstr "" + +#: src/view/com/modals/Repost.tsx:65 +msgid "Cancel quote post" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:101 +#: src/view/shell/desktop/Search.tsx:89 +msgid "Cancel search" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:132 +msgid "Cancel waitlist signup" +msgstr "" + +#: src/view/screens/Settings.tsx:222 +msgid "Change" +msgstr "" + +#: src/view/screens/Settings.tsx:517 +#: src/view/screens/Settings.tsx:526 +msgid "Change handle" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:156 +msgid "Change Handle" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:136 +msgid "Change my email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:109 +msgid "Change Your Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:155 +msgid "Check your inbox for an email with the confirmation code to enter below:" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:38 +msgid "Choose Service" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:87 +msgid "Choose the algorithms that power your experience with custom feeds." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:105 +msgid "Choose your password" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:111 +#: src/view/com/util/forms/SearchInput.tsx:73 +msgid "Clear search query" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:38 +msgid "Close alert" +msgstr "" + +#: src/view/com/util/BottomSheetCustomBackdrop.tsx:33 +msgid "Close bottom drawer" +msgstr "" + +#: src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx:26 +msgid "Close image" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:107 +msgid "Close image viewer" +msgstr "" + +#: src/view/shell/index.web.tsx:65 +msgid "Close navigation footer" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:22 +msgid "Compose reply" +msgstr "" + +#: src/view/com/modals/Confirm.tsx:76 +#: src/view/com/modals/SelfLabel.tsx:156 +#: src/view/com/modals/VerifyEmail.tsx:220 +#: src/view/screens/PreferencesHomeFeed.tsx:223 +#: src/view/screens/PreferencesThreads.tsx:128 +msgid "Confirm" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:193 +#: src/view/com/modals/ChangeEmail.tsx:195 +msgid "Confirm Change" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:34 +msgid "Confirm content language settings" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:201 +msgid "Confirm delete account" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:157 +#: src/view/com/modals/DeleteAccount.tsx:168 +#: src/view/com/modals/VerifyEmail.tsx:154 +msgid "Confirmation code" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:125 +#: src/view/com/auth/login/LoginForm.tsx:266 +msgid "Connecting..." +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:42 +msgid "Content Filtering" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:68 +msgid "Content Languages" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:68 +msgid "Content Warning" +msgstr "" + +#: src/view/com/composer/labels/LabelsBtn.tsx:32 +msgid "Content warnings" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:178 +#: src/view/com/modals/InviteCodes.tsx:129 +msgid "Copied" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:171 +msgid "Copy" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:97 +msgid "Could not load feed" +msgstr "" + +#: src/view/screens/ProfileList.tsx:788 +msgid "Could not load list" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:42 +msgid "Create a new account" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:77 +msgid "Create Account" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:39 +msgid "Create new account" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:390 +#: src/view/com/modals/ServerInput.tsx:102 +msgid "Custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:531 +msgid "Danger Zone" +msgstr "" + +#: src/view/screens/Settings.tsx:538 +msgid "Delete account" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:75 +msgid "Delete Account" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:193 +msgid "Delete app password" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:204 +msgid "Delete my account" +msgstr "" + +#: src/view/screens/Settings.tsx:548 +msgid "Delete my account…" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:203 +msgid "Deleted post." +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:209 +#: src/view/com/modals/CreateOrEditList.tsx:225 +#: src/view/com/modals/EditProfile.tsx:199 +#: src/view/com/modals/EditProfile.tsx:211 +msgid "Description" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:112 +msgid "Dev Server" +msgstr "" + +#: src/view/screens/Settings.tsx:553 +msgid "Developer Tools" +msgstr "" + +#: src/view/screens/Feeds.tsx:165 +msgid "Discover new feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:193 +msgid "Display name" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:181 +msgid "Display Name" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:488 +msgid "Domain verified!" +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:77 +#: src/view/com/modals/ContentFilteringSettings.tsx:85 +#: src/view/com/modals/crop-image/CropImage.web.tsx:152 +#: src/view/com/modals/EditImage.tsx:333 +#: src/view/com/modals/ListAddUser.tsx:153 +#: src/view/com/modals/SelfLabel.tsx:159 +#: src/view/screens/PreferencesHomeFeed.tsx:226 +#: src/view/screens/PreferencesThreads.tsx:131 +msgid "Done" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:42 +msgid "Done{extraText}" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:141 +#: src/view/com/modals/EditImage.tsx:207 +msgid "Edit image" +msgstr "" + +#: src/view/screens/Feeds.tsx:132 +msgid "Edit My Feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:158 +msgid "Edit my profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:373 +msgid "Edit profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:376 +msgid "Edit Profile" +msgstr "" + +#: src/view/screens/Feeds.tsx:92 +msgid "Edit Saved Feeds" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:89 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:145 +#: src/view/com/modals/ChangeEmail.tsx:141 +#: src/view/com/modals/Waitlist.tsx:88 +msgid "Email" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:80 +msgid "Email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:111 +msgid "Email Updated" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:123 +msgid "Enable this setting to only see replies between people you follow." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:87 +msgid "Enter the address of your provider:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:372 +msgid "Enter the domain you want to use" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:85 +msgid "Enter your email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:117 +msgid "Enter your new email address below." +msgstr "" + +#: src/view/com/auth/login/Login.tsx:96 +msgid "Enter your username and password" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:151 +msgid "Expand alt text" +msgstr "" + +#: src/view/screens/Feeds.tsx:301 +msgid "Feed offline" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:163 +msgid "Feed Preferences" +msgstr "" + +#: src/view/shell/Drawer.tsx:365 +msgid "Feedback" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:154 +#: src/view/shell/Drawer.tsx:289 +msgid "Feeds" +msgstr "" + +#: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:119 +msgid "Finding similar accounts..." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:90 +msgid "Fine-tune the content you see on your home screen." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:43 +msgid "Fine-tune the discussion threads." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:459 +msgid "Follow" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:537 +msgid "following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:443 +msgid "Following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:492 +msgid "Follows you" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:99 +msgid "For security reasons, we'll need to send a confirmation code to your email address." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:192 +msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:229 +msgid "Forgot" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:226 +msgid "Forgot password" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:126 +#: src/view/com/auth/login/Login.tsx:143 +msgid "Forgot Password" +msgstr "" + +#: src/view/com/composer/photos/SelectPhotoBtn.tsx:43 +msgid "Gallery" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:178 +msgid "Get Started" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:105 +#: src/view/shell/desktop/LeftNav.tsx:92 +msgid "Go back" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:106 +#: src/view/screens/ProfileFeed.tsx:111 +#: src/view/screens/ProfileList.tsx:797 +#: src/view/screens/ProfileList.tsx:802 +msgid "Go Back" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:178 +#: src/view/com/auth/login/LoginForm.tsx:276 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:165 +msgid "Go to next" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:270 +msgid "Handle" +msgstr "" + +#: src/view/shell/Drawer.tsx:375 +msgid "Help" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:316 +msgid "Hide" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:308 +msgid "Hide user list" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:110 +#: src/view/shell/Drawer.tsx:240 +msgid "Home" +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:69 +#: src/view/screens/Settings.tsx:423 +msgid "Home Feed Preferences" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:111 +msgid "Hosting provider" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:92 +#: src/view/com/auth/create/Step1.tsx:97 +msgid "Hosting provider address" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:203 +msgid "I have a code" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:286 +msgid "I have my own domain" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:128 +msgid "If none are selected, suitable for all ages." +msgstr "" + +#: src/view/com/modals/AltImage.tsx:96 +msgid "Image alt text" +msgstr "" + +#: src/view/com/util/UserAvatar.tsx:300 +#: src/view/com/util/UserBanner.tsx:118 +msgid "Image options" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:109 +msgid "Invalid username or password" +msgstr "" + +#: src/view/screens/Settings.tsx:336 +msgid "Invite" +msgstr "" + +#: src/view/screens/Settings.tsx:329 +msgid "Invite a Friend" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:56 +msgid "Invite code" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:67 +msgid "Join the waitlist" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:67 +#: src/view/com/auth/create/Step2.tsx:71 +msgid "Join the waitlist." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:124 +msgid "Join Waitlist" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:97 +msgid "Language selection" +msgstr "" + +#: src/view/screens/Settings.tsx:471 +msgid "Languages" +msgstr "" + +#: src/view/com/util/moderation/PostAlerts.tsx:47 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:55 +#: src/view/com/util/moderation/ScreenHider.tsx:88 +msgid "Learn More" +msgstr "" + +#: src/view/com/util/moderation/ContentHider.tsx:75 +#: src/view/com/util/moderation/PostAlerts.tsx:40 +#: src/view/com/util/moderation/PostHider.tsx:76 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:47 +#: src/view/com/util/moderation/ScreenHider.tsx:85 +msgid "Learn more about this warning" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:76 +msgid "Leave them all unchecked to see any language." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:56 +msgid "Leaving Bluesky" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:127 +#: src/view/com/auth/login/Login.tsx:144 +msgid "Let's get your password reset!" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:479 +msgid "Like this feed" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:177 +msgid "List Avatar" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:190 +msgid "List Name" +msgstr "" + +#: src/view/shell/Drawer.tsx:297 +msgid "Lists" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:220 +#: src/view/com/post-thread/PostThread.tsx:228 +msgid "Load more posts" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:194 +msgid "Load new posts" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:50 +msgid "Local dev server" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:93 +msgid "Login to account that is not listed" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:70 +msgid "Make sure this is where you intend to go!" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:71 +msgid "Menu" +msgstr "" + +#: src/view/screens/Settings.tsx:487 +#: src/view/shell/Drawer.tsx:304 +msgid "Moderation" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:469 +#: src/view/screens/ProfileFeed.tsx:312 +#: src/view/screens/ProfileList.tsx:542 +msgid "More options" +msgstr "" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:158 +msgid "More post options" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:53 +msgid "My Birthday" +msgstr "" + +#: src/view/screens/Feeds.tsx:128 +msgid "My Feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:454 +msgid "My Saved Feeds" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:162 +#: src/view/com/modals/CreateOrEditList.tsx:202 +msgid "Name" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:76 +msgid "Never lose access to your followers and data." +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:203 +#: src/view/screens/Feeds.tsx:261 +#: src/view/screens/Profile.tsx:304 +#: src/view/screens/ProfileFeed.tsx:378 +#: src/view/screens/ProfileList.tsx:210 +#: src/view/screens/ProfileList.tsx:248 +#: src/view/shell/desktop/LeftNav.tsx:229 +msgid "New post" +msgstr "" + +#: src/view/shell/desktop/LeftNav.tsx:239 +msgid "New Post" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:105 +#: src/view/com/auth/login/LoginForm.tsx:279 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:158 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:168 +msgid "Next" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:137 +msgid "Next image" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:472 +#: src/view/screens/ProfileList.tsx:667 +msgid "No description" +msgstr "" + +#: src/view/screens/Feeds.tsx:207 +msgid "No results found for \"{query}\"" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:140 +#: src/view/shell/desktop/Search.tsx:112 +msgid "No results found for {0}" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:136 +msgid "Not Applicable" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:178 +#: src/view/shell/Drawer.tsx:262 +msgid "Notifications" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:41 +msgid "Okay" +msgstr "" + +#: src/view/com/composer/Composer.tsx:334 +msgid "One or more images is missing alt text." +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:50 +msgid "Open navigation" +msgstr "" + +#: src/view/screens/Settings.tsx:463 +msgid "Opens configurable language settings" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:284 +msgid "Opens modal for using custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:482 +msgid "Opens moderation settings" +msgstr "" + +#: src/view/screens/Settings.tsx:448 +msgid "Opens screen with all saved feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:501 +msgid "Opens the app password settings page" +msgstr "" + +#: src/view/screens/Settings.tsx:415 +msgid "Opens the home feed preferences" +msgstr "" + +#: src/view/screens/Settings.tsx:580 +msgid "Opens the storybook page" +msgstr "" + +#: src/view/screens/Settings.tsx:560 +msgid "Opens the system log page" +msgstr "" + +#: src/view/screens/Settings.tsx:432 +msgid "Opens the threads preferences" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:98 +msgid "Other account" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:88 +msgid "Other service" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:84 +msgid "Other..." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:100 +#: src/view/com/auth/create/Step2.tsx:110 +#: src/view/com/auth/login/LoginForm.tsx:214 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:132 +#: src/view/com/modals/DeleteAccount.tsx:183 +msgid "Password" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:28 +msgid "Password updated!" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:122 +msgid "Pictures meant for adults." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:67 +msgid "Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:125 +msgid "Please enter a unique name for this App Password or use our randomly generated one." +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:172 +msgid "Please enter your password as well:" +msgstr "" + +#: src/view/com/composer/Composer.tsx:317 +msgid "Post" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:343 +msgid "Post hidden" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:80 +msgid "Post language" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:69 +msgid "Post Languages" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:309 +msgid "Post not found" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:51 +msgid "Potentially Misleading Link" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:123 +msgid "Previous image" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:73 +msgid "Prioritize Your Follows" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:220 +#: src/view/shell/Drawer.tsx:161 +#: src/view/shell/Drawer.tsx:325 +msgid "Profile" +msgstr "" + +#: src/view/screens/Settings.tsx:689 +msgid "Protect your account by verifying your email." +msgstr "" + +#: src/view/com/modals/Repost.tsx:53 +msgid "Quote post" +msgstr "" + +#: src/view/com/modals/Repost.tsx:57 +msgid "Quote Post" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:236 +msgid "Ratios" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:84 +msgid "Remove" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:154 +msgid "Remove image" +msgstr "" + +#: src/view/com/composer/ExternalEmbed.tsx:70 +msgid "Remove image preview" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:120 +msgid "Reply Filters" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:165 +msgid "Report {collectionName}" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:37 +msgid "Report post" +msgstr "" + +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:69 +msgid "Repost or quote post" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:181 +#: src/view/com/modals/ChangeEmail.tsx:183 +msgid "Request Change" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:52 +msgid "Required for this provider" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:110 +msgid "Reset code" +msgstr "" + +#: src/view/screens/Settings.tsx:602 +msgid "Reset onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:592 +msgid "Reset preferences state" +msgstr "" + +#: src/view/screens/Settings.tsx:600 +msgid "Resets the onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:590 +msgid "Resets the preferences state" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:114 +#: src/view/com/auth/create/CreateAccount.tsx:118 +#: src/view/com/auth/login/LoginForm.tsx:256 +#: src/view/com/auth/login/LoginForm.tsx:259 +#: src/view/com/util/error/ErrorMessage.tsx:55 +#: src/view/com/util/error/ErrorScreen.tsx:65 +msgid "Retry" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:166 +msgid "Retry change handle" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:114 +#: src/view/com/modals/BirthDateSettings.tsx:90 +#: src/view/com/modals/BirthDateSettings.tsx:93 +#: src/view/com/modals/ChangeHandle.tsx:179 +#: src/view/com/modals/CreateOrEditList.tsx:240 +#: src/view/com/modals/CreateOrEditList.tsx:248 +#: src/view/com/modals/EditProfile.tsx:225 +msgid "Save" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:105 +msgid "Save alt text" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:210 +msgid "Save changes" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:233 +msgid "Save Changes" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:176 +msgid "Save handle change" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:144 +msgid "Save image crop" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:89 +#: src/view/com/search/HeaderWithInput.tsx:101 +#: src/view/com/util/forms/SearchInput.tsx:64 +#: src/view/shell/bottom-bar/BottomBar.tsx:132 +#: src/view/shell/desktop/Search.tsx:81 +#: src/view/shell/Drawer.tsx:218 +msgid "Search" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:110 +msgid "Security Step Required" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:30 +msgid "See what's next" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:75 +msgid "Select Bluesky Social" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:115 +msgid "Select from an existing account" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:141 +msgid "Select service" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:191 +msgid "Send Confirmation Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:119 +msgid "Send email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:130 +msgid "Send Email" +msgstr "" + +#: src/view/shell/Drawer.tsx:349 +#: src/view/shell/Drawer.tsx:370 +msgid "Send feedback" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:45 +msgid "Send Report" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:80 +msgid "Set new password" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:172 +msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:101 +msgid "Set this setting to \"No\" to hide all replies from your feed." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:155 +msgid "Set this setting to \"No\" to hide all reposts from your feed." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:96 +msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:191 +msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." +msgstr "" + +#: src/view/shell/Drawer.tsx:338 +msgid "Settings" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:126 +msgid "Sexual activity or erotic nudity." +msgstr "" + +#: src/view/screens/Settings.tsx:235 +msgid "Show" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:114 +msgid "Show anyway" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:188 +msgid "Show Posts from My Feeds" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:169 +msgid "Show Quote Posts" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:98 +msgid "Show Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:76 +msgid "Show replies by people you follow before all other replies." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:152 +msgid "Show Reposts" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:337 +msgid "Show users" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:95 +#: src/view/com/auth/SplashScreen.tsx:50 +msgid "Sign in" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:53 +#: src/view/com/auth/SplashScreen.web.tsx:59 +msgid "Sign In" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:65 +msgid "Sign in as {0}" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:56 +#: src/view/com/auth/login/Login.tsx:114 +msgid "Sign in as..." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:128 +msgid "Sign into" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:71 +#: src/view/com/modals/SwitchAccount.tsx:74 +#: src/view/screens/Settings.tsx:274 +#: src/view/screens/Settings.tsx:277 +msgid "Sign out" +msgstr "" + +#: src/view/screens/Settings.tsx:245 +msgid "Signed in as" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:37 +msgid "Skip" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:51 +msgid "Sort Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:54 +msgid "Sort replies to the same post by:" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:122 +msgid "Square" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:106 +#: src/view/com/modals/ServerInput.tsx:62 +msgid "Staging" +msgstr "" + +#: src/view/screens/Settings.tsx:624 +msgid "Status page" +msgstr "" + +#: src/view/screens/Settings.tsx:582 +msgid "Storybook" +msgstr "" + +#: src/view/screens/ProfileList.tsx:533 +msgid "Subscribe" +msgstr "" + +#: src/view/screens/ProfileList.tsx:529 +msgid "Subscribe to this list" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:47 +msgid "Switch Account" +msgstr "" + +#: src/view/screens/Settings.tsx:562 +msgid "System log" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:112 +msgid "Tall" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:50 +msgid "Text input field" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:312 +msgid "The post may have been deleted." +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:71 +msgid "This {screenDescription} has been flagged:" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:58 +msgid "This information is not shared with other users." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:107 +msgid "This is important in case you ever need to change your email or reset your password." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:71 +msgid "This is the service that keeps you online." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:63 +msgid "This link is taking you to the following website:" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:176 +msgid "This post has been deleted." +msgstr "" + +#: src/view/screens/Settings.tsx:441 +msgid "Thread Preferences" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:93 +msgid "Threaded Mode" +msgstr "" + +#: src/view/com/util/forms/DropdownButton.tsx:230 +msgid "Toggle dropdown" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:271 +msgid "Transformations" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:654 +msgid "Translate" +msgstr "" + +#: src/view/com/util/error/ErrorScreen.tsx:73 +msgid "Try again" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:74 +#: src/view/com/auth/login/LoginForm.tsx:113 +msgid "Unable to contact your service. Please check your Internet connection." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:386 +#: src/view/com/profile/ProfileHeader.tsx:389 +msgid "Unblock" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:185 +msgid "Update {displayName} in Lists" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:174 +msgid "Updating..." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:456 +msgid "Upload a text file to:" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:148 +msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:516 +msgid "Use default provider" +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:37 +msgid "User handle" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:168 +#: src/view/com/auth/login/LoginForm.tsx:185 +msgid "Username or email address" +msgstr "" + +#: src/view/screens/ProfileList.tsx:694 +msgid "Users" +msgstr "" + +#: src/view/screens/Settings.tsx:650 +msgid "Verify email" +msgstr "" + +#: src/view/screens/Settings.tsx:675 +msgid "Verify my email" +msgstr "" + +#: src/view/screens/Settings.tsx:684 +msgid "Verify My Email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:205 +#: src/view/com/modals/ChangeEmail.tsx:207 +msgid "Verify New Email" +msgstr "" + +#: src/view/screens/Log.tsx:53 +msgid "View debug entry" +msgstr "" + +#: src/view/com/profile/ProfileSubpageHeader.tsx:127 +msgid "View the avatar" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:80 +msgid "Visit Site" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:78 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:168 +msgid "What is the issue with this {collectionName}?" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:72 +msgid "Which languages are used in this post?" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:71 +msgid "Which languages would you like to see in your algorithmic feeds?" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:102 +msgid "Wide" +msgstr "" + +#: src/view/com/composer/Composer.tsx:390 +msgid "Write post" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:31 +msgid "Write your reply" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:122 +msgid "You can change hosting providers at any time." +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:31 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:346 +msgid "You have blocked the author or you have been blocked by the author." +msgstr "" + +#: src/view/com/lists/ListsList.tsx:112 +msgid "You have no lists." +msgstr "" + +#: src/view/screens/AppPasswords.tsx:55 +msgid "You have not created any app passwords yet. You can create one by pressing the button below." +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:83 +msgid "You will receive an email with a \"reset code.\" Enter that code here, then enter your new password." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:42 +msgid "Your account" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:121 +msgid "Your birth date" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:107 +msgid "Your email has been saved! We'll be in touch soon." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:125 +msgid "Your email has been updated but not verified. As a next step, please verify your new email." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:102 +msgid "Your email has not yet been verified. This is an important security step which we recommend." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:41 +#: src/view/com/modals/ChangeHandle.tsx:275 +msgid "Your full handle will be" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:69 +msgid "Your hosting provider" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:63 +msgid "Your posts, likes, and blocks are public. Mutes are private." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:27 +msgid "Your user handle" +msgstr "" diff --git a/src/locale/locales/en/messages.js b/src/locale/locales/en/messages.js new file mode 100644 index 000000000..8d85ab77c --- /dev/null +++ b/src/locale/locales/en/messages.js @@ -0,0 +1 @@ +/*eslint-disable*/module.exports={messages:JSON.parse("{\"PBodTo\":\"- end of feed -\",\"EtUMsZ\":\". This warning is only available for posts with media attached.\",\"J/hVSQ\":[[\"0\"]],\"hZQerY\":[[\"0\"],\" \",[\"purposeLabel\"],\" List\"],\"6RmyWt\":\"<0>Here is your app password.</0> Use this to sign into the other app along with your handle.\",\"AnNF5e\":\"Accessibility\",\"AeXO77\":\"Account\",\"4WY4MD\":\"Account options\",\"m16xKo\":\"Add\",\"fBBX+K\":\"Add a content warning\",\"JU3hs2\":\"Add a user to this list\",\"MPPZ54\":\"Add account\",\"LkA8jz\":\"Add alt text\",\"Z8idyM\":\"Add details\",\"AoXl11\":\"Add details to report\",\"iE6B/9\":\"Add link card\",\"EXHdP1\":\"Add link card:\",\"x6laaL\":\"Add the following DNS record to your domain:\",\"jRrQFe\":\"Adjust the number of likes a reply must have to be shown in your feed.\",\"qLa52r\":\"Adult Content\",\"sxkWRg\":\"Advanced\",\"u2HO/d\":\"ALT\",\"u/DP73\":\"Alt text\",\"0QlT7/\":\"Alt text describes images for blind and low-vision users, and helps give context to everyone.\",\"woXbjq\":[\"An email has been sent to \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"Fon2dK\":[\"An email has been sent to your previous address, \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"HZFm5R\":\"and\",\"SMmUnj\":\"App passwords\",\"aAIQg2\":\"Appearance\",\"EbvWd3\":\"Artistic or non-erotic nudity.\",\"iH8pgl\":\"Back\",\"ehOkF+\":\"Basics\",\"+gCI2a\":\"Birthday\",\"pieVBA\":\"Birthday:\",\"HFCE4A\":\"Blocked post.\",\"7A9u1j\":\"Bluesky\",\"ZHmKSm\":\"Bluesky is flexible.\",\"odLrdl\":\"Bluesky is open.\",\"/LsWK4\":\"Bluesky is public.\",\"C50OGr\":\"Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon.\",\"klVoaP\":\"Bluesky.Social\",\"qxBitM\":[\"Build version \",[\"0\"],\" \",[\"1\"]],\"rT2cV+\":\"Camera\",\"JGGrPC\":\"Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long.\",\"dEgA5A\":\"Cancel\",\"aMH9rr\":\"Cancel account deletion\",\"kc3E4R\":\"Cancel add image alt text\",\"wg4LHQ\":\"Cancel change handle\",\"hFL1Li\":\"Cancel image crop\",\"tijH8t\":\"Cancel profile editing\",\"Qe4C/d\":\"Cancel quote post\",\"5TviPn\":\"Cancel search\",\"nss3UV\":\"Cancel waitlist signup\",\"o+XJ9D\":\"Change\",\"pQco5R\":\"Change handle\",\"Q5e1U/\":\"Change Handle\",\"a3NAfL\":\"Change my email\",\"4vatyk\":\"Change Your Email\",\"/+X+/K\":\"Check your inbox for an email with the confirmation code to enter below:\",\"Rt502e\":\"Choose Service\",\"/L45sc\":\"Choose the algorithms that power your experience with custom feeds.\",\"Wk8hkn\":\"Choose your password\",\"QbvBNV\":\"Clear search query\",\"flH7u/\":\"Close alert\",\"hYmnbk\":\"Close bottom drawer\",\"47L1V1\":\"Close image\",\"l49ujN\":\"Close image viewer\",\"UryHFO\":\"Close navigation footer\",\"o8UUti\":\"Compose reply\",\"7VpPHA\":\"Confirm\",\"q8upsf\":\"Confirm Change\",\"8pNKIr\":\"Confirm content language settings\",\"tGg8Kt\":\"Confirm delete account\",\"ioZOzk\":\"Confirmation code\",\"J28zul\":\"Connecting...\",\"m8j6up\":\"Content Filtering\",\"/PlAsF\":\"Content Languages\",\"cogwXi\":\"Content Warning\",\"FG7AQv\":\"Content warnings\",\"6V3Ea3\":\"Copied\",\"he3ygx\":\"Copy\",\"7wWvgo\":\"Could not load feed\",\"8NNr/O\":\"Could not load list\",\"mpt9T+\":\"Create a new account\",\"IS0nrP\":\"Create Account\",\"6HbhpU\":\"Create new account\",\"GAD3Dx\":\"Custom domain\",\"ZQKLI1\":\"Danger Zone\",\"ZDGm40\":\"Delete account\",\"vzX5FB\":\"Delete Account\",\"gUEtxf\":\"Delete app password\",\"ktknoE\":\"Delete my account\",\"szz0+N\":\"Delete my account…\",\"u+1OHY\":\"Deleted post.\",\"Nu4oKW\":\"Description\",\"dacKHE\":\"Dev Server\",\"2ygkE8\":\"Developer Tools\",\"pbLwal\":\"Discover new feeds\",\"pfa8F0\":\"Display name\",\"0gS7M5\":\"Display Name\",\"iZ5pMB\":\"Domain verified!\",\"DPfwMq\":\"Done\",\"zT97vP\":[\"Done\",[\"extraText\"]],\"XQFMOm\":\"Edit image\",\"cLmurE\":\"Edit My Feeds\",\"bRZ5XW\":\"Edit my profile\",\"9OpVZg\":\"Edit profile\",\"QJQd1J\":\"Edit Profile\",\"Jn7kox\":\"Edit Saved Feeds\",\"O3oNi5\":\"Email\",\"ATGYL1\":\"Email address\",\"pJJ0Vp\":\"Email Updated\",\"96mted\":\"Enable this setting to only see replies between people you follow.\",\"YbIxza\":\"Enter the address of your provider:\",\"BfIgP6\":\"Enter the domain you want to use\",\"xRPn3U\":\"Enter your email address\",\"+inPGm\":\"Enter your new email address below.\",\"T0KLp4\":\"Enter your username and password\",\"0PkE20\":\"Expand alt text\",\"4yCy8i\":\"Feed offline\",\"N0CqyO\":\"Feed Preferences\",\"YirHq7\":\"Feedback\",\"2DoBvq\":\"Feeds\",\"Qzj1WT\":\"Finding similar accounts...\",\"QKSrQV\":\"Fine-tune the content you see on your home screen.\",\"r+KeyR\":\"Fine-tune the discussion threads.\",\"MKEPCY\":\"Follow\",\"NIjL2Y\":\"following\",\"y6sq5j\":\"Following\",\"p3UO/y\":\"Follows you\",\"5RhDkD\":\"For security reasons, we'll need to send a confirmation code to your email address.\",\"NJPhAO\":\"For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one.\",\"5bDfuq\":\"Forgot\",\"hEPLrs\":\"Forgot password\",\"dn8X5t\":\"Forgot Password\",\"U+kFad\":\"Gallery\",\"c3b0B0\":\"Get Started\",\"CKyk7Q\":\"Go back\",\"sr0UJD\":\"Go Back\",\"Rtp0y7\":\"Go to next\",\"Nf7oXL\":\"Handle\",\"c3XJ18\":\"Help\",\"vLyv1R\":\"Hide\",\"qdOx2q\":\"Hide user list\",\"i0qMbr\":\"Home\",\"sXZ8IU\":\"Home Feed Preferences\",\"yt7fhu\":\"Hosting provider\",\"s2xA6t\":\"Hosting provider address\",\"o+axy6\":\"I have a code\",\"wey2os\":\"I have my own domain\",\"WlEcKr\":\"If none are selected, suitable for all ages.\",\"VCk0rR\":\"Image alt text\",\"STGpNQ\":\"Image options\",\"dSKHAa\":\"Invalid username or password\",\"MFKlMB\":\"Invite\",\"F5MZVk\":\"Invite a Friend\",\"6KlkHI\":\"Invite code\",\"F75w8j\":\"Join the waitlist\",\"6iVTdm\":\"Join the waitlist.\",\"SNzppu\":\"Join Waitlist\",\"Dcq5kL\":\"Language selection\",\"GAmD3h\":\"Languages\",\"NgeSlx\":\"Learn More\",\"rj0Lke\":\"Learn more about this warning\",\"kq2ga7\":\"Leave them all unchecked to see any language.\",\"QfDITI\":\"Leaving Bluesky\",\"Esfg1M\":\"Let's get your password reset!\",\"BvSY1i\":\"Like this feed\",\"FuZWua\":\"List Avatar\",\"8mjA4F\":\"List Name\",\"h16FyT\":\"Lists\",\"ujW4FW\":\"Load more posts\",\"VkLESX\":\"Load new posts\",\"jl0AFf\":\"Local dev server\",\"cR9UpQ\":\"Login to account that is not listed\",\"2U/gDT\":\"Make sure this is where you intend to go!\",\"zucql+\":\"Menu\",\"DzmsLV\":\"Moderation\",\"3Siwmw\":\"More options\",\"Y17r45\":\"More post options\",\"Mysqyf\":\"My Birthday\",\"6MBNS/\":\"My Feeds\",\"Ha6iBv\":\"My Saved Feeds\",\"6YtxFj\":\"Name\",\"8yolS6\":\"Never lose access to your followers and data.\",\"2B7HLH\":\"New post\",\"FGrimz\":\"New Post\",\"hXzOVo\":\"Next\",\"EatZYJ\":\"Next image\",\"flmDTf\":\"No description\",\"fOlAiK\":[\"No results found for \\\"\",[\"query\"],\"\\\"\"],\"kA9DpB\":[\"No results found for \",[\"0\"]],\"ispbnl\":\"Not Applicable\",\"iDNBZe\":\"Notifications\",\"UaXeX3\":\"Okay\",\"Cqo2D+\":\"One or more images is missing alt text.\",\"M/Q2aG\":\"Open navigation\",\"M5PuNq\":\"Opens configurable language settings\",\"eSqpax\":\"Opens modal for using custom domain\",\"vYwHHI\":\"Opens moderation settings\",\"0tHyB7\":\"Opens screen with all saved feeds\",\"nmRoY/\":\"Opens the app password settings page\",\"6e9Apv\":\"Opens the home feed preferences\",\"O87Dr/\":\"Opens the storybook page\",\"G+PVmg\":\"Opens the system log page\",\"Jqb7sy\":\"Opens the threads preferences\",\"b22AVl\":\"Other account\",\"n+HLOP\":\"Other service\",\"1PKxQ7\":\"Other...\",\"8ZsakT\":\"Password\",\"DKeVgZ\":\"Password updated!\",\"VeZE5Q\":\"Pictures meant for adults.\",\"Apyknf\":\"Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed.\",\"9qpQ5O\":\"Please enter a unique name for this App Password or use our randomly generated one.\",\"QJr5Xp\":\"Please enter your password as well:\",\"y28hnO\":\"Post\",\"h5RcXU\":\"Post hidden\",\"r5zLS0\":\"Post language\",\"AzCucI\":\"Post Languages\",\"tJFPmV\":\"Post not found\",\"0+DQbr\":\"Potentially Misleading Link\",\"MHk+7g\":\"Previous image\",\"x8iR7V\":\"Prioritize Your Follows\",\"vERlcd\":\"Profile\",\"MrgqOW\":\"Protect your account by verifying your email.\",\"8HFFRQ\":\"Quote post\",\"+KrAHa\":\"Quote Post\",\"WlWsdE\":\"Ratios\",\"t/YqKh\":\"Remove\",\"5ywtDz\":\"Remove image\",\"Dw/XUh\":\"Remove image preview\",\"Veu9gK\":\"Reply Filters\",\"lQWbAr\":[\"Report \",[\"collectionName\"]],\"6IcSvC\":\"Report post\",\"JOV5dR\":\"Repost or quote post\",\"bqG37Z\":\"Request Change\",\"8XIT+P\":\"Required for this provider\",\"vJgYMA\":\"Reset code\",\"xEL92I\":\"Reset onboarding state\",\"bee/Fw\":\"Reset preferences state\",\"wToeoz\":\"Resets the onboarding state\",\"nIU7qI\":\"Resets the preferences state\",\"6gRgw8\":\"Retry\",\"hAbYQa\":\"Retry change handle\",\"tfDRzk\":\"Save\",\"KV2YQQ\":\"Save alt text\",\"y3aU20\":\"Save changes\",\"IUwGEM\":\"Save Changes\",\"Xs07Tg\":\"Save handle change\",\"BckA7m\":\"Save image crop\",\"A1taO8\":\"Search\",\"CKROFy\":\"Security Step Required\",\"cNzyJW\":\"See what's next\",\"L5sM7N\":\"Select Bluesky Social\",\"o3dwub\":\"Select from an existing account\",\"GGw2AK\":\"Select service\",\"vp9yIB\":\"Send Confirmation Email\",\"65dxv8\":\"Send email\",\"i/TzEU\":\"Send Email\",\"RoafuO\":\"Send feedback\",\"4cijjm\":\"Send Report\",\"V/e7nf\":\"Set new password\",\"gwsie4\":\"Set this setting to \\\"No\\\" to hide all quote posts from your feed. Reposts will still be visible.\",\"IZjC3J\":\"Set this setting to \\\"No\\\" to hide all replies from your feed.\",\"KIgU3l\":\"Set this setting to \\\"No\\\" to hide all reposts from your feed.\",\"zaAyrz\":\"Set this setting to \\\"Yes\\\" to show replies in a threaded view. This is an experimental feature.\",\"fQV2eE\":\"Set this setting to \\\"Yes\\\" to show samples of your saved feeds in your following feed. This is an experimental feature.\",\"Tz0i8g\":\"Settings\",\"HfWHhJ\":\"Sexual activity or erotic nudity.\",\"8vETh9\":\"Show\",\"aWAdCb\":\"Show anyway\",\"NijgXr\":\"Show Posts from My Feeds\",\"T3Mt8m\":\"Show Quote Posts\",\"BlW8X/\":\"Show Replies\",\"X4GwDb\":\"Show replies by people you follow before all other replies.\",\"GiogzH\":\"Show Reposts\",\"fhY/fL\":\"Show users\",\"5lWFkC\":\"Sign in\",\"n1ekoW\":\"Sign In\",\"N9o7n5\":[\"Sign in as \",[\"0\"]],\"FT1MVS\":\"Sign in as...\",\"+UpfFC\":\"Sign into\",\"fcWrnU\":\"Sign out\",\"zU+Ro7\":\"Signed in as\",\"6Uau97\":\"Skip\",\"0o5BFH\":\"Sort Replies\",\"GH1Rgk\":\"Sort replies to the same post by:\",\"1DA6ap\":\"Square\",\"aKEHLj\":\"Staging\",\"tgEXwM\":\"Status page\",\"P5jja7\":\"Storybook\",\"EDl9kS\":\"Subscribe\",\"5z3ICN\":\"Subscribe to this list\",\"VjWeLI\":\"Switch Account\",\"fP8jTZ\":\"System log\",\"HF6Iah\":\"Tall\",\"p8Iz39\":\"Text input field\",\"G4EksE\":\"The post may have been deleted.\",\"KRYn8w\":[\"This \",[\"screenDescription\"],\" has been flagged:\"],\"lm845B\":\"This information is not shared with other users.\",\"5Pvw/O\":\"This is important in case you ever need to change your email or reset your password.\",\"sQQfZ9\":\"This is the service that keeps you online.\",\"CvX8qs\":\"This link is taking you to the following website:\",\"WKrUVy\":\"This post has been deleted.\",\"u9ThjD\":\"Thread Preferences\",\"zmXsk5\":\"Threaded Mode\",\"1x30Qt\":\"Toggle dropdown\",\"KFXQEt\":\"Transformations\",\"pi8x/S\":\"Translate\",\"KDw4GX\":\"Try again\",\"nc4Wfd\":\"Unable to contact your service. Please check your Internet connection.\",\"tuS5Jz\":\"Unblock\",\"vaz2uI\":[\"Update \",[\"displayName\"],\" in Lists\"],\"RXbEvi\":\"Updating...\",\"Vwkfp4\":\"Upload a text file to:\",\"jTdnU6\":\"Use app passwords to login to other Bluesky clients without giving full access to your account or password.\",\"CH1am9\":\"Use default provider\",\"t4Yp4Z\":\"User handle\",\"nZx9mr\":\"Username or email address\",\"Sxm8rQ\":\"Users\",\"MBOY4U\":\"Verify email\",\"Ejyv0o\":\"Verify my email\",\"9czCrB\":\"Verify My Email\",\"ibSVGR\":\"Verify New Email\",\"nHsQde\":\"View debug entry\",\"47jzzd\":\"View the avatar\",\"wK4H1r\":\"Visit Site\",\"qjBGxf\":\"We're so excited to have you join us!\",\"Mj7rl/\":[\"What is the issue with this \",[\"collectionName\"],\"?\"],\"3qn29J\":\"Which languages are used in this post?\",\"uawiGa\":\"Which languages would you like to see in your algorithmic feeds?\",\"I5S9ZE\":\"Wide\",\"y02THm\":\"Write post\",\"6ckZRB\":\"Write your reply\",\"STPj0e\":\"You can change hosting providers at any time.\",\"67nRLM\":\"You can now sign in with your new password.\",\"RkXibf\":\"You have blocked the author or you have been blocked by the author.\",\"tCLJ9E\":\"You have no lists.\",\"NDgp3i\":\"You have not created any app passwords yet. You can create one by pressing the button below.\",\"RrDyEb\":\"You will receive an email with a \\\"reset code.\\\" Enter that code here, then enter your new password.\",\"gdRnT7\":\"Your account\",\"k7hmsH\":\"Your birth date\",\"OubkcP\":\"Your email has been saved! We'll be in touch soon.\",\"z2L+/9\":\"Your email has been updated but not verified. As a next step, please verify your new email.\",\"XZlIVw\":\"Your email has not yet been verified. This is an important security step which we recommend.\",\"qv9f4I\":\"Your full handle will be\",\"lvcqqG\":\"Your hosting provider\",\"Oqt/PG\":\"Your posts, likes, and blocks are public. Mutes are private.\",\"MvWO9d\":\"Your user handle\"}")}; \ No newline at end of file diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po new file mode 100644 index 000000000..586303586 --- /dev/null +++ b/src/locale/locales/en/messages.po @@ -0,0 +1,1544 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-11-05 16:01-0800\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: en\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/view/screens/Profile.tsx:212 +msgid "- end of feed -" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:138 +msgid ". This warning is only available for posts with media attached." +msgstr "" + +#: src/view/com/modals/Repost.tsx:45 +msgid "{0}" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:167 +msgid "{0} {purposeLabel} List" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:132 +msgid "<0>Here is your app password.</0> Use this to sign into the other app along with your handle." +msgstr "" + +#: src/view/com/modals/EditImage.tsx:299 +#: src/view/screens/Settings.tsx:363 +msgid "Accessibility" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:157 +#: src/view/screens/Settings.tsx:201 +msgid "Account" +msgstr "" + +#: src/view/com/util/AccountDropdownBtn.tsx:41 +msgid "Account options" +msgstr "" + +#: src/view/screens/ProfileList.tsx:710 +msgid "Add" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:57 +msgid "Add a content warning" +msgstr "" + +#: src/view/screens/ProfileList.tsx:700 +msgid "Add a user to this list" +msgstr "" + +#: src/view/screens/Settings.tsx:313 +#: src/view/screens/Settings.tsx:322 +msgid "Add account" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:119 +#: src/view/com/composer/photos/Gallery.tsx:167 +msgid "Add alt text" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:41 +#: src/view/com/modals/report/Modal.tsx:190 +msgid "Add details" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:193 +msgid "Add details to report" +msgstr "" + +#: src/view/com/composer/Composer.tsx:419 +msgid "Add link card" +msgstr "" + +#: src/view/com/composer/Composer.tsx:422 +msgid "Add link card:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:418 +msgid "Add the following DNS record to your domain:" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:140 +msgid "Adjust the number of likes a reply must have to be shown in your feed." +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:76 +msgid "Adult Content" +msgstr "" + +#: src/view/screens/Settings.tsx:493 +msgid "Advanced" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:127 +msgid "ALT" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:315 +msgid "Alt text" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:193 +msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:112 +msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:119 +msgid "An email has been sent to your previous address, {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:238 +msgid "and" +msgstr "" + +#: src/view/screens/Settings.tsx:509 +msgid "App passwords" +msgstr "" + +#: src/view/screens/Settings.tsx:378 +msgid "Appearance" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:124 +msgid "Artistic or non-erotic nudity." +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:92 +#: src/view/com/auth/login/ChooseAccountForm.tsx:111 +#: src/view/com/auth/login/LoginForm.tsx:247 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:150 +#: src/view/com/modals/report/InputIssueDetails.tsx:45 +#: src/view/com/post-thread/PostThread.tsx:317 +#: src/view/com/post-thread/PostThread.tsx:325 +#: src/view/com/post-thread/PostThread.tsx:354 +#: src/view/com/post-thread/PostThread.tsx:362 +#: src/view/com/profile/ProfileHeader.tsx:576 +msgid "Back" +msgstr "" + +#: src/view/screens/Settings.tsx:407 +msgid "Basics" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:130 +#: src/view/com/modals/BirthDateSettings.tsx:69 +msgid "Birthday" +msgstr "" + +#: src/view/screens/Settings.tsx:228 +msgid "Birthday:" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:211 +msgid "Blocked post." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:52 +#: src/view/com/auth/SplashScreen.tsx:27 +msgid "Bluesky" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:84 +msgid "Bluesky is flexible." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:73 +msgid "Bluesky is open." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:60 +msgid "Bluesky is public." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:70 +msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon." +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:78 +msgid "Bluesky.Social" +msgstr "" + +#: src/view/screens/Settings.tsx:612 +msgid "Build version {0} {1}" +msgstr "" + +#: src/view/com/composer/photos/OpenCameraBtn.tsx:62 +msgid "Camera" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:199 +msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long." +msgstr "" + +#: src/view/com/composer/Composer.tsx:271 +#: src/view/com/composer/Composer.tsx:274 +#: src/view/com/modals/AltImage.tsx:127 +#: src/view/com/modals/ChangeEmail.tsx:216 +#: src/view/com/modals/ChangeEmail.tsx:218 +#: src/view/com/modals/Confirm.tsx:89 +#: src/view/com/modals/CreateOrEditList.tsx:258 +#: src/view/com/modals/CreateOrEditList.tsx:263 +#: src/view/com/modals/DeleteAccount.tsx:142 +#: src/view/com/modals/DeleteAccount.tsx:215 +#: src/view/com/modals/EditImage.tsx:323 +#: src/view/com/modals/EditProfile.tsx:250 +#: src/view/com/modals/LinkWarning.tsx:90 +#: src/view/com/modals/Repost.tsx:74 +#: src/view/com/modals/UserAddRemoveLists.tsx:199 +#: src/view/com/modals/Waitlist.tsx:136 +#: src/view/com/search/HeaderWithInput.tsx:127 +#: src/view/shell/desktop/Search.tsx:93 +msgid "Cancel" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:138 +#: src/view/com/modals/DeleteAccount.tsx:211 +msgid "Cancel account deletion" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:122 +msgid "Cancel add image alt text" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:144 +msgid "Cancel change handle" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:134 +msgid "Cancel image crop" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:245 +msgid "Cancel profile editing" +msgstr "" + +#: src/view/com/modals/Repost.tsx:65 +msgid "Cancel quote post" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:101 +#: src/view/shell/desktop/Search.tsx:89 +msgid "Cancel search" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:132 +msgid "Cancel waitlist signup" +msgstr "" + +#: src/view/screens/Settings.tsx:222 +msgid "Change" +msgstr "" + +#: src/view/screens/Settings.tsx:517 +#: src/view/screens/Settings.tsx:526 +msgid "Change handle" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:156 +msgid "Change Handle" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:136 +msgid "Change my email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:109 +msgid "Change Your Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:155 +msgid "Check your inbox for an email with the confirmation code to enter below:" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:38 +msgid "Choose Service" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:87 +msgid "Choose the algorithms that power your experience with custom feeds." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:105 +msgid "Choose your password" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:111 +#: src/view/com/util/forms/SearchInput.tsx:73 +msgid "Clear search query" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:38 +msgid "Close alert" +msgstr "" + +#: src/view/com/util/BottomSheetCustomBackdrop.tsx:33 +msgid "Close bottom drawer" +msgstr "" + +#: src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx:26 +msgid "Close image" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:107 +msgid "Close image viewer" +msgstr "" + +#: src/view/shell/index.web.tsx:65 +msgid "Close navigation footer" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:22 +msgid "Compose reply" +msgstr "" + +#: src/view/com/modals/Confirm.tsx:76 +#: src/view/com/modals/SelfLabel.tsx:156 +#: src/view/com/modals/VerifyEmail.tsx:220 +#: src/view/screens/PreferencesHomeFeed.tsx:223 +#: src/view/screens/PreferencesThreads.tsx:128 +msgid "Confirm" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:193 +#: src/view/com/modals/ChangeEmail.tsx:195 +msgid "Confirm Change" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:34 +msgid "Confirm content language settings" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:201 +msgid "Confirm delete account" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:157 +#: src/view/com/modals/DeleteAccount.tsx:168 +#: src/view/com/modals/VerifyEmail.tsx:154 +msgid "Confirmation code" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:125 +#: src/view/com/auth/login/LoginForm.tsx:266 +msgid "Connecting..." +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:42 +msgid "Content Filtering" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:68 +msgid "Content Languages" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:68 +msgid "Content Warning" +msgstr "" + +#: src/view/com/composer/labels/LabelsBtn.tsx:32 +msgid "Content warnings" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:178 +#: src/view/com/modals/InviteCodes.tsx:129 +msgid "Copied" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:171 +msgid "Copy" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:97 +msgid "Could not load feed" +msgstr "" + +#: src/view/screens/ProfileList.tsx:788 +msgid "Could not load list" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:42 +msgid "Create a new account" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:77 +msgid "Create Account" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:39 +msgid "Create new account" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:390 +#: src/view/com/modals/ServerInput.tsx:102 +msgid "Custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:531 +msgid "Danger Zone" +msgstr "" + +#: src/view/screens/Settings.tsx:538 +msgid "Delete account" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:75 +msgid "Delete Account" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:193 +msgid "Delete app password" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:204 +msgid "Delete my account" +msgstr "" + +#: src/view/screens/Settings.tsx:548 +msgid "Delete my account…" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:203 +msgid "Deleted post." +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:209 +#: src/view/com/modals/CreateOrEditList.tsx:225 +#: src/view/com/modals/EditProfile.tsx:199 +#: src/view/com/modals/EditProfile.tsx:211 +msgid "Description" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:112 +msgid "Dev Server" +msgstr "" + +#: src/view/screens/Settings.tsx:553 +msgid "Developer Tools" +msgstr "" + +#: src/view/screens/Feeds.tsx:165 +msgid "Discover new feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:193 +msgid "Display name" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:181 +msgid "Display Name" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:488 +msgid "Domain verified!" +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:77 +#: src/view/com/modals/ContentFilteringSettings.tsx:85 +#: src/view/com/modals/crop-image/CropImage.web.tsx:152 +#: src/view/com/modals/EditImage.tsx:333 +#: src/view/com/modals/ListAddUser.tsx:153 +#: src/view/com/modals/SelfLabel.tsx:159 +#: src/view/screens/PreferencesHomeFeed.tsx:226 +#: src/view/screens/PreferencesThreads.tsx:131 +msgid "Done" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:42 +msgid "Done{extraText}" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:141 +#: src/view/com/modals/EditImage.tsx:207 +msgid "Edit image" +msgstr "" + +#: src/view/screens/Feeds.tsx:132 +msgid "Edit My Feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:158 +msgid "Edit my profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:373 +msgid "Edit profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:376 +msgid "Edit Profile" +msgstr "" + +#: src/view/screens/Feeds.tsx:92 +msgid "Edit Saved Feeds" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:89 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:145 +#: src/view/com/modals/ChangeEmail.tsx:141 +#: src/view/com/modals/Waitlist.tsx:88 +msgid "Email" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:80 +msgid "Email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:111 +msgid "Email Updated" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:123 +msgid "Enable this setting to only see replies between people you follow." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:87 +msgid "Enter the address of your provider:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:372 +msgid "Enter the domain you want to use" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:85 +msgid "Enter your email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:117 +msgid "Enter your new email address below." +msgstr "" + +#: src/view/com/auth/login/Login.tsx:96 +msgid "Enter your username and password" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:151 +msgid "Expand alt text" +msgstr "" + +#: src/view/screens/Feeds.tsx:301 +msgid "Feed offline" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:163 +msgid "Feed Preferences" +msgstr "" + +#: src/view/shell/Drawer.tsx:365 +msgid "Feedback" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:154 +#: src/view/shell/Drawer.tsx:289 +msgid "Feeds" +msgstr "" + +#: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:119 +msgid "Finding similar accounts..." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:90 +msgid "Fine-tune the content you see on your home screen." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:43 +msgid "Fine-tune the discussion threads." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:459 +msgid "Follow" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:537 +msgid "following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:443 +msgid "Following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:492 +msgid "Follows you" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:99 +msgid "For security reasons, we'll need to send a confirmation code to your email address." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:192 +msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:229 +msgid "Forgot" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:226 +msgid "Forgot password" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:126 +#: src/view/com/auth/login/Login.tsx:143 +msgid "Forgot Password" +msgstr "" + +#: src/view/com/composer/photos/SelectPhotoBtn.tsx:43 +msgid "Gallery" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:178 +msgid "Get Started" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:105 +#: src/view/shell/desktop/LeftNav.tsx:92 +msgid "Go back" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:106 +#: src/view/screens/ProfileFeed.tsx:111 +#: src/view/screens/ProfileList.tsx:797 +#: src/view/screens/ProfileList.tsx:802 +msgid "Go Back" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:178 +#: src/view/com/auth/login/LoginForm.tsx:276 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:165 +msgid "Go to next" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:270 +msgid "Handle" +msgstr "" + +#: src/view/shell/Drawer.tsx:375 +msgid "Help" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:316 +msgid "Hide" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:308 +msgid "Hide user list" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:110 +#: src/view/shell/Drawer.tsx:240 +msgid "Home" +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:69 +#: src/view/screens/Settings.tsx:423 +msgid "Home Feed Preferences" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:111 +msgid "Hosting provider" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:92 +#: src/view/com/auth/create/Step1.tsx:97 +msgid "Hosting provider address" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:203 +msgid "I have a code" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:286 +msgid "I have my own domain" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:128 +msgid "If none are selected, suitable for all ages." +msgstr "" + +#: src/view/com/modals/AltImage.tsx:96 +msgid "Image alt text" +msgstr "" + +#: src/view/com/util/UserAvatar.tsx:300 +#: src/view/com/util/UserBanner.tsx:118 +msgid "Image options" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:109 +msgid "Invalid username or password" +msgstr "" + +#: src/view/screens/Settings.tsx:336 +msgid "Invite" +msgstr "" + +#: src/view/screens/Settings.tsx:329 +msgid "Invite a Friend" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:56 +msgid "Invite code" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:67 +msgid "Join the waitlist" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:67 +#: src/view/com/auth/create/Step2.tsx:71 +msgid "Join the waitlist." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:124 +msgid "Join Waitlist" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:97 +msgid "Language selection" +msgstr "" + +#: src/view/screens/Settings.tsx:471 +msgid "Languages" +msgstr "" + +#: src/view/com/util/moderation/PostAlerts.tsx:47 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:55 +#: src/view/com/util/moderation/ScreenHider.tsx:88 +msgid "Learn More" +msgstr "" + +#: src/view/com/util/moderation/ContentHider.tsx:75 +#: src/view/com/util/moderation/PostAlerts.tsx:40 +#: src/view/com/util/moderation/PostHider.tsx:76 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:47 +#: src/view/com/util/moderation/ScreenHider.tsx:85 +msgid "Learn more about this warning" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:76 +msgid "Leave them all unchecked to see any language." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:56 +msgid "Leaving Bluesky" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:127 +#: src/view/com/auth/login/Login.tsx:144 +msgid "Let's get your password reset!" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:479 +msgid "Like this feed" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:177 +msgid "List Avatar" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:190 +msgid "List Name" +msgstr "" + +#: src/view/shell/Drawer.tsx:297 +msgid "Lists" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:220 +#: src/view/com/post-thread/PostThread.tsx:228 +msgid "Load more posts" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:194 +msgid "Load new posts" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:50 +msgid "Local dev server" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:93 +msgid "Login to account that is not listed" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:70 +msgid "Make sure this is where you intend to go!" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:71 +msgid "Menu" +msgstr "" + +#: src/view/screens/Settings.tsx:487 +#: src/view/shell/Drawer.tsx:304 +msgid "Moderation" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:469 +#: src/view/screens/ProfileFeed.tsx:312 +#: src/view/screens/ProfileList.tsx:542 +msgid "More options" +msgstr "" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:158 +msgid "More post options" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:53 +msgid "My Birthday" +msgstr "" + +#: src/view/screens/Feeds.tsx:128 +msgid "My Feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:454 +msgid "My Saved Feeds" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:162 +#: src/view/com/modals/CreateOrEditList.tsx:202 +msgid "Name" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:76 +msgid "Never lose access to your followers and data." +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:203 +#: src/view/screens/Feeds.tsx:261 +#: src/view/screens/Profile.tsx:304 +#: src/view/screens/ProfileFeed.tsx:378 +#: src/view/screens/ProfileList.tsx:210 +#: src/view/screens/ProfileList.tsx:248 +#: src/view/shell/desktop/LeftNav.tsx:229 +msgid "New post" +msgstr "" + +#: src/view/shell/desktop/LeftNav.tsx:239 +msgid "New Post" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:105 +#: src/view/com/auth/login/LoginForm.tsx:279 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:158 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:168 +msgid "Next" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:137 +msgid "Next image" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:472 +#: src/view/screens/ProfileList.tsx:667 +msgid "No description" +msgstr "" + +#: src/view/screens/Feeds.tsx:207 +msgid "No results found for \"{query}\"" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:140 +#: src/view/shell/desktop/Search.tsx:112 +msgid "No results found for {0}" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:136 +msgid "Not Applicable" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:178 +#: src/view/shell/Drawer.tsx:262 +msgid "Notifications" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:41 +msgid "Okay" +msgstr "" + +#: src/view/com/composer/Composer.tsx:334 +msgid "One or more images is missing alt text." +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:50 +msgid "Open navigation" +msgstr "" + +#: src/view/screens/Settings.tsx:463 +msgid "Opens configurable language settings" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:284 +msgid "Opens modal for using custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:482 +msgid "Opens moderation settings" +msgstr "" + +#: src/view/screens/Settings.tsx:448 +msgid "Opens screen with all saved feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:501 +msgid "Opens the app password settings page" +msgstr "" + +#: src/view/screens/Settings.tsx:415 +msgid "Opens the home feed preferences" +msgstr "" + +#: src/view/screens/Settings.tsx:580 +msgid "Opens the storybook page" +msgstr "" + +#: src/view/screens/Settings.tsx:560 +msgid "Opens the system log page" +msgstr "" + +#: src/view/screens/Settings.tsx:432 +msgid "Opens the threads preferences" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:98 +msgid "Other account" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:88 +msgid "Other service" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:84 +msgid "Other..." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:100 +#: src/view/com/auth/create/Step2.tsx:110 +#: src/view/com/auth/login/LoginForm.tsx:214 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:132 +#: src/view/com/modals/DeleteAccount.tsx:183 +msgid "Password" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:28 +msgid "Password updated!" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:122 +msgid "Pictures meant for adults." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:67 +msgid "Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:125 +msgid "Please enter a unique name for this App Password or use our randomly generated one." +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:172 +msgid "Please enter your password as well:" +msgstr "" + +#: src/view/com/composer/Composer.tsx:317 +msgid "Post" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:343 +msgid "Post hidden" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:80 +msgid "Post language" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:69 +msgid "Post Languages" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:309 +msgid "Post not found" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:51 +msgid "Potentially Misleading Link" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:123 +msgid "Previous image" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:73 +msgid "Prioritize Your Follows" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:220 +#: src/view/shell/Drawer.tsx:161 +#: src/view/shell/Drawer.tsx:325 +msgid "Profile" +msgstr "" + +#: src/view/screens/Settings.tsx:689 +msgid "Protect your account by verifying your email." +msgstr "" + +#: src/view/com/modals/Repost.tsx:53 +msgid "Quote post" +msgstr "" + +#: src/view/com/modals/Repost.tsx:57 +msgid "Quote Post" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:236 +msgid "Ratios" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:84 +msgid "Remove" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:154 +msgid "Remove image" +msgstr "" + +#: src/view/com/composer/ExternalEmbed.tsx:70 +msgid "Remove image preview" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:120 +msgid "Reply Filters" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:165 +msgid "Report {collectionName}" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:37 +msgid "Report post" +msgstr "" + +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:69 +msgid "Repost or quote post" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:181 +#: src/view/com/modals/ChangeEmail.tsx:183 +msgid "Request Change" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:52 +msgid "Required for this provider" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:110 +msgid "Reset code" +msgstr "" + +#: src/view/screens/Settings.tsx:602 +msgid "Reset onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:592 +msgid "Reset preferences state" +msgstr "" + +#: src/view/screens/Settings.tsx:600 +msgid "Resets the onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:590 +msgid "Resets the preferences state" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:114 +#: src/view/com/auth/create/CreateAccount.tsx:118 +#: src/view/com/auth/login/LoginForm.tsx:256 +#: src/view/com/auth/login/LoginForm.tsx:259 +#: src/view/com/util/error/ErrorMessage.tsx:55 +#: src/view/com/util/error/ErrorScreen.tsx:65 +msgid "Retry" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:166 +msgid "Retry change handle" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:114 +#: src/view/com/modals/BirthDateSettings.tsx:90 +#: src/view/com/modals/BirthDateSettings.tsx:93 +#: src/view/com/modals/ChangeHandle.tsx:179 +#: src/view/com/modals/CreateOrEditList.tsx:240 +#: src/view/com/modals/CreateOrEditList.tsx:248 +#: src/view/com/modals/EditProfile.tsx:225 +msgid "Save" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:105 +msgid "Save alt text" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:210 +msgid "Save changes" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:233 +msgid "Save Changes" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:176 +msgid "Save handle change" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:144 +msgid "Save image crop" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:89 +#: src/view/com/search/HeaderWithInput.tsx:101 +#: src/view/com/util/forms/SearchInput.tsx:64 +#: src/view/shell/bottom-bar/BottomBar.tsx:132 +#: src/view/shell/desktop/Search.tsx:81 +#: src/view/shell/Drawer.tsx:218 +msgid "Search" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:110 +msgid "Security Step Required" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:30 +msgid "See what's next" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:75 +msgid "Select Bluesky Social" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:115 +msgid "Select from an existing account" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:141 +msgid "Select service" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:191 +msgid "Send Confirmation Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:119 +msgid "Send email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:130 +msgid "Send Email" +msgstr "" + +#: src/view/shell/Drawer.tsx:349 +#: src/view/shell/Drawer.tsx:370 +msgid "Send feedback" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:45 +msgid "Send Report" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:80 +msgid "Set new password" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:172 +msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:101 +msgid "Set this setting to \"No\" to hide all replies from your feed." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:155 +msgid "Set this setting to \"No\" to hide all reposts from your feed." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:96 +msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:191 +msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." +msgstr "" + +#: src/view/shell/Drawer.tsx:338 +msgid "Settings" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:126 +msgid "Sexual activity or erotic nudity." +msgstr "" + +#: src/view/screens/Settings.tsx:235 +msgid "Show" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:114 +msgid "Show anyway" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:188 +msgid "Show Posts from My Feeds" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:169 +msgid "Show Quote Posts" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:98 +msgid "Show Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:76 +msgid "Show replies by people you follow before all other replies." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:152 +msgid "Show Reposts" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:337 +msgid "Show users" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:95 +#: src/view/com/auth/SplashScreen.tsx:50 +msgid "Sign in" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:53 +#: src/view/com/auth/SplashScreen.web.tsx:59 +msgid "Sign In" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:65 +msgid "Sign in as {0}" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:56 +#: src/view/com/auth/login/Login.tsx:114 +msgid "Sign in as..." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:128 +msgid "Sign into" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:71 +#: src/view/com/modals/SwitchAccount.tsx:74 +#: src/view/screens/Settings.tsx:274 +#: src/view/screens/Settings.tsx:277 +msgid "Sign out" +msgstr "" + +#: src/view/screens/Settings.tsx:245 +msgid "Signed in as" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:37 +msgid "Skip" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:51 +msgid "Sort Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:54 +msgid "Sort replies to the same post by:" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:122 +msgid "Square" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:106 +#: src/view/com/modals/ServerInput.tsx:62 +msgid "Staging" +msgstr "" + +#: src/view/screens/Settings.tsx:624 +msgid "Status page" +msgstr "" + +#: src/view/screens/Settings.tsx:582 +msgid "Storybook" +msgstr "" + +#: src/view/screens/ProfileList.tsx:533 +msgid "Subscribe" +msgstr "" + +#: src/view/screens/ProfileList.tsx:529 +msgid "Subscribe to this list" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:47 +msgid "Switch Account" +msgstr "" + +#: src/view/screens/Settings.tsx:562 +msgid "System log" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:112 +msgid "Tall" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:50 +msgid "Text input field" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:312 +msgid "The post may have been deleted." +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:71 +msgid "This {screenDescription} has been flagged:" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:58 +msgid "This information is not shared with other users." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:107 +msgid "This is important in case you ever need to change your email or reset your password." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:71 +msgid "This is the service that keeps you online." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:63 +msgid "This link is taking you to the following website:" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:176 +msgid "This post has been deleted." +msgstr "" + +#: src/view/screens/Settings.tsx:441 +msgid "Thread Preferences" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:93 +msgid "Threaded Mode" +msgstr "" + +#: src/view/com/util/forms/DropdownButton.tsx:230 +msgid "Toggle dropdown" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:271 +msgid "Transformations" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:654 +msgid "Translate" +msgstr "" + +#: src/view/com/util/error/ErrorScreen.tsx:73 +msgid "Try again" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:74 +#: src/view/com/auth/login/LoginForm.tsx:113 +msgid "Unable to contact your service. Please check your Internet connection." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:386 +#: src/view/com/profile/ProfileHeader.tsx:389 +msgid "Unblock" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:185 +msgid "Update {displayName} in Lists" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:174 +msgid "Updating..." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:456 +msgid "Upload a text file to:" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:148 +msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:516 +msgid "Use default provider" +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:37 +msgid "User handle" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:168 +#: src/view/com/auth/login/LoginForm.tsx:185 +msgid "Username or email address" +msgstr "" + +#: src/view/screens/ProfileList.tsx:694 +msgid "Users" +msgstr "" + +#: src/view/screens/Settings.tsx:650 +msgid "Verify email" +msgstr "" + +#: src/view/screens/Settings.tsx:675 +msgid "Verify my email" +msgstr "" + +#: src/view/screens/Settings.tsx:684 +msgid "Verify My Email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:205 +#: src/view/com/modals/ChangeEmail.tsx:207 +msgid "Verify New Email" +msgstr "" + +#: src/view/screens/Log.tsx:53 +msgid "View debug entry" +msgstr "" + +#: src/view/com/profile/ProfileSubpageHeader.tsx:127 +msgid "View the avatar" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:80 +msgid "Visit Site" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:78 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:168 +msgid "What is the issue with this {collectionName}?" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:72 +msgid "Which languages are used in this post?" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:71 +msgid "Which languages would you like to see in your algorithmic feeds?" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:102 +msgid "Wide" +msgstr "" + +#: src/view/com/composer/Composer.tsx:390 +msgid "Write post" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:31 +msgid "Write your reply" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:122 +msgid "You can change hosting providers at any time." +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:31 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:346 +msgid "You have blocked the author or you have been blocked by the author." +msgstr "" + +#: src/view/com/lists/ListsList.tsx:112 +msgid "You have no lists." +msgstr "" + +#: src/view/screens/AppPasswords.tsx:55 +msgid "You have not created any app passwords yet. You can create one by pressing the button below." +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:83 +msgid "You will receive an email with a \"reset code.\" Enter that code here, then enter your new password." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:42 +msgid "Your account" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:121 +msgid "Your birth date" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:107 +msgid "Your email has been saved! We'll be in touch soon." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:125 +msgid "Your email has been updated but not verified. As a next step, please verify your new email." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:102 +msgid "Your email has not yet been verified. This is an important security step which we recommend." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:41 +#: src/view/com/modals/ChangeHandle.tsx:275 +msgid "Your full handle will be" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:69 +msgid "Your hosting provider" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:63 +msgid "Your posts, likes, and blocks are public. Mutes are private." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:27 +msgid "Your user handle" +msgstr "" diff --git a/src/locale/locales/es/messages.js b/src/locale/locales/es/messages.js new file mode 100644 index 000000000..8d85ab77c --- /dev/null +++ b/src/locale/locales/es/messages.js @@ -0,0 +1 @@ +/*eslint-disable*/module.exports={messages:JSON.parse("{\"PBodTo\":\"- end of feed -\",\"EtUMsZ\":\". This warning is only available for posts with media attached.\",\"J/hVSQ\":[[\"0\"]],\"hZQerY\":[[\"0\"],\" \",[\"purposeLabel\"],\" List\"],\"6RmyWt\":\"<0>Here is your app password.</0> Use this to sign into the other app along with your handle.\",\"AnNF5e\":\"Accessibility\",\"AeXO77\":\"Account\",\"4WY4MD\":\"Account options\",\"m16xKo\":\"Add\",\"fBBX+K\":\"Add a content warning\",\"JU3hs2\":\"Add a user to this list\",\"MPPZ54\":\"Add account\",\"LkA8jz\":\"Add alt text\",\"Z8idyM\":\"Add details\",\"AoXl11\":\"Add details to report\",\"iE6B/9\":\"Add link card\",\"EXHdP1\":\"Add link card:\",\"x6laaL\":\"Add the following DNS record to your domain:\",\"jRrQFe\":\"Adjust the number of likes a reply must have to be shown in your feed.\",\"qLa52r\":\"Adult Content\",\"sxkWRg\":\"Advanced\",\"u2HO/d\":\"ALT\",\"u/DP73\":\"Alt text\",\"0QlT7/\":\"Alt text describes images for blind and low-vision users, and helps give context to everyone.\",\"woXbjq\":[\"An email has been sent to \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"Fon2dK\":[\"An email has been sent to your previous address, \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"HZFm5R\":\"and\",\"SMmUnj\":\"App passwords\",\"aAIQg2\":\"Appearance\",\"EbvWd3\":\"Artistic or non-erotic nudity.\",\"iH8pgl\":\"Back\",\"ehOkF+\":\"Basics\",\"+gCI2a\":\"Birthday\",\"pieVBA\":\"Birthday:\",\"HFCE4A\":\"Blocked post.\",\"7A9u1j\":\"Bluesky\",\"ZHmKSm\":\"Bluesky is flexible.\",\"odLrdl\":\"Bluesky is open.\",\"/LsWK4\":\"Bluesky is public.\",\"C50OGr\":\"Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon.\",\"klVoaP\":\"Bluesky.Social\",\"qxBitM\":[\"Build version \",[\"0\"],\" \",[\"1\"]],\"rT2cV+\":\"Camera\",\"JGGrPC\":\"Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long.\",\"dEgA5A\":\"Cancel\",\"aMH9rr\":\"Cancel account deletion\",\"kc3E4R\":\"Cancel add image alt text\",\"wg4LHQ\":\"Cancel change handle\",\"hFL1Li\":\"Cancel image crop\",\"tijH8t\":\"Cancel profile editing\",\"Qe4C/d\":\"Cancel quote post\",\"5TviPn\":\"Cancel search\",\"nss3UV\":\"Cancel waitlist signup\",\"o+XJ9D\":\"Change\",\"pQco5R\":\"Change handle\",\"Q5e1U/\":\"Change Handle\",\"a3NAfL\":\"Change my email\",\"4vatyk\":\"Change Your Email\",\"/+X+/K\":\"Check your inbox for an email with the confirmation code to enter below:\",\"Rt502e\":\"Choose Service\",\"/L45sc\":\"Choose the algorithms that power your experience with custom feeds.\",\"Wk8hkn\":\"Choose your password\",\"QbvBNV\":\"Clear search query\",\"flH7u/\":\"Close alert\",\"hYmnbk\":\"Close bottom drawer\",\"47L1V1\":\"Close image\",\"l49ujN\":\"Close image viewer\",\"UryHFO\":\"Close navigation footer\",\"o8UUti\":\"Compose reply\",\"7VpPHA\":\"Confirm\",\"q8upsf\":\"Confirm Change\",\"8pNKIr\":\"Confirm content language settings\",\"tGg8Kt\":\"Confirm delete account\",\"ioZOzk\":\"Confirmation code\",\"J28zul\":\"Connecting...\",\"m8j6up\":\"Content Filtering\",\"/PlAsF\":\"Content Languages\",\"cogwXi\":\"Content Warning\",\"FG7AQv\":\"Content warnings\",\"6V3Ea3\":\"Copied\",\"he3ygx\":\"Copy\",\"7wWvgo\":\"Could not load feed\",\"8NNr/O\":\"Could not load list\",\"mpt9T+\":\"Create a new account\",\"IS0nrP\":\"Create Account\",\"6HbhpU\":\"Create new account\",\"GAD3Dx\":\"Custom domain\",\"ZQKLI1\":\"Danger Zone\",\"ZDGm40\":\"Delete account\",\"vzX5FB\":\"Delete Account\",\"gUEtxf\":\"Delete app password\",\"ktknoE\":\"Delete my account\",\"szz0+N\":\"Delete my account…\",\"u+1OHY\":\"Deleted post.\",\"Nu4oKW\":\"Description\",\"dacKHE\":\"Dev Server\",\"2ygkE8\":\"Developer Tools\",\"pbLwal\":\"Discover new feeds\",\"pfa8F0\":\"Display name\",\"0gS7M5\":\"Display Name\",\"iZ5pMB\":\"Domain verified!\",\"DPfwMq\":\"Done\",\"zT97vP\":[\"Done\",[\"extraText\"]],\"XQFMOm\":\"Edit image\",\"cLmurE\":\"Edit My Feeds\",\"bRZ5XW\":\"Edit my profile\",\"9OpVZg\":\"Edit profile\",\"QJQd1J\":\"Edit Profile\",\"Jn7kox\":\"Edit Saved Feeds\",\"O3oNi5\":\"Email\",\"ATGYL1\":\"Email address\",\"pJJ0Vp\":\"Email Updated\",\"96mted\":\"Enable this setting to only see replies between people you follow.\",\"YbIxza\":\"Enter the address of your provider:\",\"BfIgP6\":\"Enter the domain you want to use\",\"xRPn3U\":\"Enter your email address\",\"+inPGm\":\"Enter your new email address below.\",\"T0KLp4\":\"Enter your username and password\",\"0PkE20\":\"Expand alt text\",\"4yCy8i\":\"Feed offline\",\"N0CqyO\":\"Feed Preferences\",\"YirHq7\":\"Feedback\",\"2DoBvq\":\"Feeds\",\"Qzj1WT\":\"Finding similar accounts...\",\"QKSrQV\":\"Fine-tune the content you see on your home screen.\",\"r+KeyR\":\"Fine-tune the discussion threads.\",\"MKEPCY\":\"Follow\",\"NIjL2Y\":\"following\",\"y6sq5j\":\"Following\",\"p3UO/y\":\"Follows you\",\"5RhDkD\":\"For security reasons, we'll need to send a confirmation code to your email address.\",\"NJPhAO\":\"For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one.\",\"5bDfuq\":\"Forgot\",\"hEPLrs\":\"Forgot password\",\"dn8X5t\":\"Forgot Password\",\"U+kFad\":\"Gallery\",\"c3b0B0\":\"Get Started\",\"CKyk7Q\":\"Go back\",\"sr0UJD\":\"Go Back\",\"Rtp0y7\":\"Go to next\",\"Nf7oXL\":\"Handle\",\"c3XJ18\":\"Help\",\"vLyv1R\":\"Hide\",\"qdOx2q\":\"Hide user list\",\"i0qMbr\":\"Home\",\"sXZ8IU\":\"Home Feed Preferences\",\"yt7fhu\":\"Hosting provider\",\"s2xA6t\":\"Hosting provider address\",\"o+axy6\":\"I have a code\",\"wey2os\":\"I have my own domain\",\"WlEcKr\":\"If none are selected, suitable for all ages.\",\"VCk0rR\":\"Image alt text\",\"STGpNQ\":\"Image options\",\"dSKHAa\":\"Invalid username or password\",\"MFKlMB\":\"Invite\",\"F5MZVk\":\"Invite a Friend\",\"6KlkHI\":\"Invite code\",\"F75w8j\":\"Join the waitlist\",\"6iVTdm\":\"Join the waitlist.\",\"SNzppu\":\"Join Waitlist\",\"Dcq5kL\":\"Language selection\",\"GAmD3h\":\"Languages\",\"NgeSlx\":\"Learn More\",\"rj0Lke\":\"Learn more about this warning\",\"kq2ga7\":\"Leave them all unchecked to see any language.\",\"QfDITI\":\"Leaving Bluesky\",\"Esfg1M\":\"Let's get your password reset!\",\"BvSY1i\":\"Like this feed\",\"FuZWua\":\"List Avatar\",\"8mjA4F\":\"List Name\",\"h16FyT\":\"Lists\",\"ujW4FW\":\"Load more posts\",\"VkLESX\":\"Load new posts\",\"jl0AFf\":\"Local dev server\",\"cR9UpQ\":\"Login to account that is not listed\",\"2U/gDT\":\"Make sure this is where you intend to go!\",\"zucql+\":\"Menu\",\"DzmsLV\":\"Moderation\",\"3Siwmw\":\"More options\",\"Y17r45\":\"More post options\",\"Mysqyf\":\"My Birthday\",\"6MBNS/\":\"My Feeds\",\"Ha6iBv\":\"My Saved Feeds\",\"6YtxFj\":\"Name\",\"8yolS6\":\"Never lose access to your followers and data.\",\"2B7HLH\":\"New post\",\"FGrimz\":\"New Post\",\"hXzOVo\":\"Next\",\"EatZYJ\":\"Next image\",\"flmDTf\":\"No description\",\"fOlAiK\":[\"No results found for \\\"\",[\"query\"],\"\\\"\"],\"kA9DpB\":[\"No results found for \",[\"0\"]],\"ispbnl\":\"Not Applicable\",\"iDNBZe\":\"Notifications\",\"UaXeX3\":\"Okay\",\"Cqo2D+\":\"One or more images is missing alt text.\",\"M/Q2aG\":\"Open navigation\",\"M5PuNq\":\"Opens configurable language settings\",\"eSqpax\":\"Opens modal for using custom domain\",\"vYwHHI\":\"Opens moderation settings\",\"0tHyB7\":\"Opens screen with all saved feeds\",\"nmRoY/\":\"Opens the app password settings page\",\"6e9Apv\":\"Opens the home feed preferences\",\"O87Dr/\":\"Opens the storybook page\",\"G+PVmg\":\"Opens the system log page\",\"Jqb7sy\":\"Opens the threads preferences\",\"b22AVl\":\"Other account\",\"n+HLOP\":\"Other service\",\"1PKxQ7\":\"Other...\",\"8ZsakT\":\"Password\",\"DKeVgZ\":\"Password updated!\",\"VeZE5Q\":\"Pictures meant for adults.\",\"Apyknf\":\"Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed.\",\"9qpQ5O\":\"Please enter a unique name for this App Password or use our randomly generated one.\",\"QJr5Xp\":\"Please enter your password as well:\",\"y28hnO\":\"Post\",\"h5RcXU\":\"Post hidden\",\"r5zLS0\":\"Post language\",\"AzCucI\":\"Post Languages\",\"tJFPmV\":\"Post not found\",\"0+DQbr\":\"Potentially Misleading Link\",\"MHk+7g\":\"Previous image\",\"x8iR7V\":\"Prioritize Your Follows\",\"vERlcd\":\"Profile\",\"MrgqOW\":\"Protect your account by verifying your email.\",\"8HFFRQ\":\"Quote post\",\"+KrAHa\":\"Quote Post\",\"WlWsdE\":\"Ratios\",\"t/YqKh\":\"Remove\",\"5ywtDz\":\"Remove image\",\"Dw/XUh\":\"Remove image preview\",\"Veu9gK\":\"Reply Filters\",\"lQWbAr\":[\"Report \",[\"collectionName\"]],\"6IcSvC\":\"Report post\",\"JOV5dR\":\"Repost or quote post\",\"bqG37Z\":\"Request Change\",\"8XIT+P\":\"Required for this provider\",\"vJgYMA\":\"Reset code\",\"xEL92I\":\"Reset onboarding state\",\"bee/Fw\":\"Reset preferences state\",\"wToeoz\":\"Resets the onboarding state\",\"nIU7qI\":\"Resets the preferences state\",\"6gRgw8\":\"Retry\",\"hAbYQa\":\"Retry change handle\",\"tfDRzk\":\"Save\",\"KV2YQQ\":\"Save alt text\",\"y3aU20\":\"Save changes\",\"IUwGEM\":\"Save Changes\",\"Xs07Tg\":\"Save handle change\",\"BckA7m\":\"Save image crop\",\"A1taO8\":\"Search\",\"CKROFy\":\"Security Step Required\",\"cNzyJW\":\"See what's next\",\"L5sM7N\":\"Select Bluesky Social\",\"o3dwub\":\"Select from an existing account\",\"GGw2AK\":\"Select service\",\"vp9yIB\":\"Send Confirmation Email\",\"65dxv8\":\"Send email\",\"i/TzEU\":\"Send Email\",\"RoafuO\":\"Send feedback\",\"4cijjm\":\"Send Report\",\"V/e7nf\":\"Set new password\",\"gwsie4\":\"Set this setting to \\\"No\\\" to hide all quote posts from your feed. Reposts will still be visible.\",\"IZjC3J\":\"Set this setting to \\\"No\\\" to hide all replies from your feed.\",\"KIgU3l\":\"Set this setting to \\\"No\\\" to hide all reposts from your feed.\",\"zaAyrz\":\"Set this setting to \\\"Yes\\\" to show replies in a threaded view. This is an experimental feature.\",\"fQV2eE\":\"Set this setting to \\\"Yes\\\" to show samples of your saved feeds in your following feed. This is an experimental feature.\",\"Tz0i8g\":\"Settings\",\"HfWHhJ\":\"Sexual activity or erotic nudity.\",\"8vETh9\":\"Show\",\"aWAdCb\":\"Show anyway\",\"NijgXr\":\"Show Posts from My Feeds\",\"T3Mt8m\":\"Show Quote Posts\",\"BlW8X/\":\"Show Replies\",\"X4GwDb\":\"Show replies by people you follow before all other replies.\",\"GiogzH\":\"Show Reposts\",\"fhY/fL\":\"Show users\",\"5lWFkC\":\"Sign in\",\"n1ekoW\":\"Sign In\",\"N9o7n5\":[\"Sign in as \",[\"0\"]],\"FT1MVS\":\"Sign in as...\",\"+UpfFC\":\"Sign into\",\"fcWrnU\":\"Sign out\",\"zU+Ro7\":\"Signed in as\",\"6Uau97\":\"Skip\",\"0o5BFH\":\"Sort Replies\",\"GH1Rgk\":\"Sort replies to the same post by:\",\"1DA6ap\":\"Square\",\"aKEHLj\":\"Staging\",\"tgEXwM\":\"Status page\",\"P5jja7\":\"Storybook\",\"EDl9kS\":\"Subscribe\",\"5z3ICN\":\"Subscribe to this list\",\"VjWeLI\":\"Switch Account\",\"fP8jTZ\":\"System log\",\"HF6Iah\":\"Tall\",\"p8Iz39\":\"Text input field\",\"G4EksE\":\"The post may have been deleted.\",\"KRYn8w\":[\"This \",[\"screenDescription\"],\" has been flagged:\"],\"lm845B\":\"This information is not shared with other users.\",\"5Pvw/O\":\"This is important in case you ever need to change your email or reset your password.\",\"sQQfZ9\":\"This is the service that keeps you online.\",\"CvX8qs\":\"This link is taking you to the following website:\",\"WKrUVy\":\"This post has been deleted.\",\"u9ThjD\":\"Thread Preferences\",\"zmXsk5\":\"Threaded Mode\",\"1x30Qt\":\"Toggle dropdown\",\"KFXQEt\":\"Transformations\",\"pi8x/S\":\"Translate\",\"KDw4GX\":\"Try again\",\"nc4Wfd\":\"Unable to contact your service. Please check your Internet connection.\",\"tuS5Jz\":\"Unblock\",\"vaz2uI\":[\"Update \",[\"displayName\"],\" in Lists\"],\"RXbEvi\":\"Updating...\",\"Vwkfp4\":\"Upload a text file to:\",\"jTdnU6\":\"Use app passwords to login to other Bluesky clients without giving full access to your account or password.\",\"CH1am9\":\"Use default provider\",\"t4Yp4Z\":\"User handle\",\"nZx9mr\":\"Username or email address\",\"Sxm8rQ\":\"Users\",\"MBOY4U\":\"Verify email\",\"Ejyv0o\":\"Verify my email\",\"9czCrB\":\"Verify My Email\",\"ibSVGR\":\"Verify New Email\",\"nHsQde\":\"View debug entry\",\"47jzzd\":\"View the avatar\",\"wK4H1r\":\"Visit Site\",\"qjBGxf\":\"We're so excited to have you join us!\",\"Mj7rl/\":[\"What is the issue with this \",[\"collectionName\"],\"?\"],\"3qn29J\":\"Which languages are used in this post?\",\"uawiGa\":\"Which languages would you like to see in your algorithmic feeds?\",\"I5S9ZE\":\"Wide\",\"y02THm\":\"Write post\",\"6ckZRB\":\"Write your reply\",\"STPj0e\":\"You can change hosting providers at any time.\",\"67nRLM\":\"You can now sign in with your new password.\",\"RkXibf\":\"You have blocked the author or you have been blocked by the author.\",\"tCLJ9E\":\"You have no lists.\",\"NDgp3i\":\"You have not created any app passwords yet. You can create one by pressing the button below.\",\"RrDyEb\":\"You will receive an email with a \\\"reset code.\\\" Enter that code here, then enter your new password.\",\"gdRnT7\":\"Your account\",\"k7hmsH\":\"Your birth date\",\"OubkcP\":\"Your email has been saved! We'll be in touch soon.\",\"z2L+/9\":\"Your email has been updated but not verified. As a next step, please verify your new email.\",\"XZlIVw\":\"Your email has not yet been verified. This is an important security step which we recommend.\",\"qv9f4I\":\"Your full handle will be\",\"lvcqqG\":\"Your hosting provider\",\"Oqt/PG\":\"Your posts, likes, and blocks are public. Mutes are private.\",\"MvWO9d\":\"Your user handle\"}")}; \ No newline at end of file diff --git a/src/locale/locales/es/messages.po b/src/locale/locales/es/messages.po new file mode 100644 index 000000000..daeb14912 --- /dev/null +++ b/src/locale/locales/es/messages.po @@ -0,0 +1,1544 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-11-06 12:28-0800\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: es\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/view/screens/Profile.tsx:212 +msgid "- end of feed -" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:138 +msgid ". This warning is only available for posts with media attached." +msgstr "" + +#: src/view/com/modals/Repost.tsx:45 +msgid "{0}" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:167 +msgid "{0} {purposeLabel} List" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:132 +msgid "<0>Here is your app password.</0> Use this to sign into the other app along with your handle." +msgstr "" + +#: src/view/com/modals/EditImage.tsx:299 +#: src/view/screens/Settings.tsx:363 +msgid "Accessibility" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:157 +#: src/view/screens/Settings.tsx:201 +msgid "Account" +msgstr "" + +#: src/view/com/util/AccountDropdownBtn.tsx:41 +msgid "Account options" +msgstr "" + +#: src/view/screens/ProfileList.tsx:710 +msgid "Add" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:57 +msgid "Add a content warning" +msgstr "" + +#: src/view/screens/ProfileList.tsx:700 +msgid "Add a user to this list" +msgstr "" + +#: src/view/screens/Settings.tsx:313 +#: src/view/screens/Settings.tsx:322 +msgid "Add account" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:119 +#: src/view/com/composer/photos/Gallery.tsx:167 +msgid "Add alt text" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:41 +#: src/view/com/modals/report/Modal.tsx:190 +msgid "Add details" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:193 +msgid "Add details to report" +msgstr "" + +#: src/view/com/composer/Composer.tsx:419 +msgid "Add link card" +msgstr "" + +#: src/view/com/composer/Composer.tsx:422 +msgid "Add link card:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:418 +msgid "Add the following DNS record to your domain:" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:140 +msgid "Adjust the number of likes a reply must have to be shown in your feed." +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:76 +msgid "Adult Content" +msgstr "" + +#: src/view/screens/Settings.tsx:493 +msgid "Advanced" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:127 +msgid "ALT" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:315 +msgid "Alt text" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:193 +msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:112 +msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:119 +msgid "An email has been sent to your previous address, {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:238 +msgid "and" +msgstr "" + +#: src/view/screens/Settings.tsx:509 +msgid "App passwords" +msgstr "" + +#: src/view/screens/Settings.tsx:378 +msgid "Appearance" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:124 +msgid "Artistic or non-erotic nudity." +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:92 +#: src/view/com/auth/login/ChooseAccountForm.tsx:111 +#: src/view/com/auth/login/LoginForm.tsx:247 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:150 +#: src/view/com/modals/report/InputIssueDetails.tsx:45 +#: src/view/com/post-thread/PostThread.tsx:317 +#: src/view/com/post-thread/PostThread.tsx:325 +#: src/view/com/post-thread/PostThread.tsx:354 +#: src/view/com/post-thread/PostThread.tsx:362 +#: src/view/com/profile/ProfileHeader.tsx:576 +msgid "Back" +msgstr "" + +#: src/view/screens/Settings.tsx:407 +msgid "Basics" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:130 +#: src/view/com/modals/BirthDateSettings.tsx:69 +msgid "Birthday" +msgstr "" + +#: src/view/screens/Settings.tsx:228 +msgid "Birthday:" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:211 +msgid "Blocked post." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:52 +#: src/view/com/auth/SplashScreen.tsx:27 +msgid "Bluesky" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:84 +msgid "Bluesky is flexible." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:73 +msgid "Bluesky is open." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:60 +msgid "Bluesky is public." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:70 +msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon." +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:78 +msgid "Bluesky.Social" +msgstr "" + +#: src/view/screens/Settings.tsx:612 +msgid "Build version {0} {1}" +msgstr "" + +#: src/view/com/composer/photos/OpenCameraBtn.tsx:62 +msgid "Camera" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:199 +msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long." +msgstr "" + +#: src/view/com/composer/Composer.tsx:271 +#: src/view/com/composer/Composer.tsx:274 +#: src/view/com/modals/AltImage.tsx:127 +#: src/view/com/modals/ChangeEmail.tsx:216 +#: src/view/com/modals/ChangeEmail.tsx:218 +#: src/view/com/modals/Confirm.tsx:89 +#: src/view/com/modals/CreateOrEditList.tsx:258 +#: src/view/com/modals/CreateOrEditList.tsx:263 +#: src/view/com/modals/DeleteAccount.tsx:142 +#: src/view/com/modals/DeleteAccount.tsx:215 +#: src/view/com/modals/EditImage.tsx:323 +#: src/view/com/modals/EditProfile.tsx:250 +#: src/view/com/modals/LinkWarning.tsx:90 +#: src/view/com/modals/Repost.tsx:74 +#: src/view/com/modals/UserAddRemoveLists.tsx:199 +#: src/view/com/modals/Waitlist.tsx:136 +#: src/view/com/search/HeaderWithInput.tsx:127 +#: src/view/shell/desktop/Search.tsx:93 +msgid "Cancel" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:138 +#: src/view/com/modals/DeleteAccount.tsx:211 +msgid "Cancel account deletion" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:122 +msgid "Cancel add image alt text" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:144 +msgid "Cancel change handle" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:134 +msgid "Cancel image crop" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:245 +msgid "Cancel profile editing" +msgstr "" + +#: src/view/com/modals/Repost.tsx:65 +msgid "Cancel quote post" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:101 +#: src/view/shell/desktop/Search.tsx:89 +msgid "Cancel search" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:132 +msgid "Cancel waitlist signup" +msgstr "" + +#: src/view/screens/Settings.tsx:222 +msgid "Change" +msgstr "" + +#: src/view/screens/Settings.tsx:517 +#: src/view/screens/Settings.tsx:526 +msgid "Change handle" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:156 +msgid "Change Handle" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:136 +msgid "Change my email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:109 +msgid "Change Your Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:155 +msgid "Check your inbox for an email with the confirmation code to enter below:" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:38 +msgid "Choose Service" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:87 +msgid "Choose the algorithms that power your experience with custom feeds." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:105 +msgid "Choose your password" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:111 +#: src/view/com/util/forms/SearchInput.tsx:73 +msgid "Clear search query" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:38 +msgid "Close alert" +msgstr "" + +#: src/view/com/util/BottomSheetCustomBackdrop.tsx:33 +msgid "Close bottom drawer" +msgstr "" + +#: src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx:26 +msgid "Close image" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:107 +msgid "Close image viewer" +msgstr "" + +#: src/view/shell/index.web.tsx:65 +msgid "Close navigation footer" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:22 +msgid "Compose reply" +msgstr "" + +#: src/view/com/modals/Confirm.tsx:76 +#: src/view/com/modals/SelfLabel.tsx:156 +#: src/view/com/modals/VerifyEmail.tsx:220 +#: src/view/screens/PreferencesHomeFeed.tsx:223 +#: src/view/screens/PreferencesThreads.tsx:128 +msgid "Confirm" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:193 +#: src/view/com/modals/ChangeEmail.tsx:195 +msgid "Confirm Change" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:34 +msgid "Confirm content language settings" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:201 +msgid "Confirm delete account" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:157 +#: src/view/com/modals/DeleteAccount.tsx:168 +#: src/view/com/modals/VerifyEmail.tsx:154 +msgid "Confirmation code" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:125 +#: src/view/com/auth/login/LoginForm.tsx:266 +msgid "Connecting..." +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:42 +msgid "Content Filtering" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:68 +msgid "Content Languages" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:68 +msgid "Content Warning" +msgstr "" + +#: src/view/com/composer/labels/LabelsBtn.tsx:32 +msgid "Content warnings" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:178 +#: src/view/com/modals/InviteCodes.tsx:129 +msgid "Copied" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:171 +msgid "Copy" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:97 +msgid "Could not load feed" +msgstr "" + +#: src/view/screens/ProfileList.tsx:788 +msgid "Could not load list" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:42 +msgid "Create a new account" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:77 +msgid "Create Account" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:39 +msgid "Create new account" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:390 +#: src/view/com/modals/ServerInput.tsx:102 +msgid "Custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:531 +msgid "Danger Zone" +msgstr "" + +#: src/view/screens/Settings.tsx:538 +msgid "Delete account" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:75 +msgid "Delete Account" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:193 +msgid "Delete app password" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:204 +msgid "Delete my account" +msgstr "" + +#: src/view/screens/Settings.tsx:548 +msgid "Delete my account…" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:203 +msgid "Deleted post." +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:209 +#: src/view/com/modals/CreateOrEditList.tsx:225 +#: src/view/com/modals/EditProfile.tsx:199 +#: src/view/com/modals/EditProfile.tsx:211 +msgid "Description" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:112 +msgid "Dev Server" +msgstr "" + +#: src/view/screens/Settings.tsx:553 +msgid "Developer Tools" +msgstr "" + +#: src/view/screens/Feeds.tsx:165 +msgid "Discover new feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:193 +msgid "Display name" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:181 +msgid "Display Name" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:488 +msgid "Domain verified!" +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:77 +#: src/view/com/modals/ContentFilteringSettings.tsx:85 +#: src/view/com/modals/crop-image/CropImage.web.tsx:152 +#: src/view/com/modals/EditImage.tsx:333 +#: src/view/com/modals/ListAddUser.tsx:153 +#: src/view/com/modals/SelfLabel.tsx:159 +#: src/view/screens/PreferencesHomeFeed.tsx:226 +#: src/view/screens/PreferencesThreads.tsx:131 +msgid "Done" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:42 +msgid "Done{extraText}" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:141 +#: src/view/com/modals/EditImage.tsx:207 +msgid "Edit image" +msgstr "" + +#: src/view/screens/Feeds.tsx:132 +msgid "Edit My Feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:158 +msgid "Edit my profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:373 +msgid "Edit profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:376 +msgid "Edit Profile" +msgstr "" + +#: src/view/screens/Feeds.tsx:92 +msgid "Edit Saved Feeds" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:89 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:145 +#: src/view/com/modals/ChangeEmail.tsx:141 +#: src/view/com/modals/Waitlist.tsx:88 +msgid "Email" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:80 +msgid "Email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:111 +msgid "Email Updated" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:123 +msgid "Enable this setting to only see replies between people you follow." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:87 +msgid "Enter the address of your provider:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:372 +msgid "Enter the domain you want to use" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:85 +msgid "Enter your email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:117 +msgid "Enter your new email address below." +msgstr "" + +#: src/view/com/auth/login/Login.tsx:96 +msgid "Enter your username and password" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:151 +msgid "Expand alt text" +msgstr "" + +#: src/view/screens/Feeds.tsx:301 +msgid "Feed offline" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:163 +msgid "Feed Preferences" +msgstr "" + +#: src/view/shell/Drawer.tsx:365 +msgid "Feedback" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:154 +#: src/view/shell/Drawer.tsx:289 +msgid "Feeds" +msgstr "" + +#: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:119 +msgid "Finding similar accounts..." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:90 +msgid "Fine-tune the content you see on your home screen." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:43 +msgid "Fine-tune the discussion threads." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:459 +msgid "Follow" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:537 +msgid "following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:443 +msgid "Following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:492 +msgid "Follows you" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:99 +msgid "For security reasons, we'll need to send a confirmation code to your email address." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:192 +msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:229 +msgid "Forgot" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:226 +msgid "Forgot password" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:126 +#: src/view/com/auth/login/Login.tsx:143 +msgid "Forgot Password" +msgstr "" + +#: src/view/com/composer/photos/SelectPhotoBtn.tsx:43 +msgid "Gallery" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:178 +msgid "Get Started" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:105 +#: src/view/shell/desktop/LeftNav.tsx:92 +msgid "Go back" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:106 +#: src/view/screens/ProfileFeed.tsx:111 +#: src/view/screens/ProfileList.tsx:797 +#: src/view/screens/ProfileList.tsx:802 +msgid "Go Back" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:178 +#: src/view/com/auth/login/LoginForm.tsx:276 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:165 +msgid "Go to next" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:270 +msgid "Handle" +msgstr "" + +#: src/view/shell/Drawer.tsx:375 +msgid "Help" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:316 +msgid "Hide" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:308 +msgid "Hide user list" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:110 +#: src/view/shell/Drawer.tsx:240 +msgid "Home" +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:69 +#: src/view/screens/Settings.tsx:423 +msgid "Home Feed Preferences" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:111 +msgid "Hosting provider" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:92 +#: src/view/com/auth/create/Step1.tsx:97 +msgid "Hosting provider address" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:203 +msgid "I have a code" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:286 +msgid "I have my own domain" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:128 +msgid "If none are selected, suitable for all ages." +msgstr "" + +#: src/view/com/modals/AltImage.tsx:96 +msgid "Image alt text" +msgstr "" + +#: src/view/com/util/UserAvatar.tsx:300 +#: src/view/com/util/UserBanner.tsx:118 +msgid "Image options" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:109 +msgid "Invalid username or password" +msgstr "" + +#: src/view/screens/Settings.tsx:336 +msgid "Invite" +msgstr "" + +#: src/view/screens/Settings.tsx:329 +msgid "Invite a Friend" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:56 +msgid "Invite code" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:67 +msgid "Join the waitlist" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:67 +#: src/view/com/auth/create/Step2.tsx:71 +msgid "Join the waitlist." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:124 +msgid "Join Waitlist" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:97 +msgid "Language selection" +msgstr "" + +#: src/view/screens/Settings.tsx:471 +msgid "Languages" +msgstr "" + +#: src/view/com/util/moderation/PostAlerts.tsx:47 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:55 +#: src/view/com/util/moderation/ScreenHider.tsx:88 +msgid "Learn More" +msgstr "" + +#: src/view/com/util/moderation/ContentHider.tsx:75 +#: src/view/com/util/moderation/PostAlerts.tsx:40 +#: src/view/com/util/moderation/PostHider.tsx:76 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:47 +#: src/view/com/util/moderation/ScreenHider.tsx:85 +msgid "Learn more about this warning" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:76 +msgid "Leave them all unchecked to see any language." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:56 +msgid "Leaving Bluesky" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:127 +#: src/view/com/auth/login/Login.tsx:144 +msgid "Let's get your password reset!" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:479 +msgid "Like this feed" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:177 +msgid "List Avatar" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:190 +msgid "List Name" +msgstr "" + +#: src/view/shell/Drawer.tsx:297 +msgid "Lists" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:220 +#: src/view/com/post-thread/PostThread.tsx:228 +msgid "Load more posts" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:194 +msgid "Load new posts" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:50 +msgid "Local dev server" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:93 +msgid "Login to account that is not listed" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:70 +msgid "Make sure this is where you intend to go!" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:71 +msgid "Menu" +msgstr "" + +#: src/view/screens/Settings.tsx:487 +#: src/view/shell/Drawer.tsx:304 +msgid "Moderation" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:469 +#: src/view/screens/ProfileFeed.tsx:312 +#: src/view/screens/ProfileList.tsx:542 +msgid "More options" +msgstr "" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:158 +msgid "More post options" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:53 +msgid "My Birthday" +msgstr "" + +#: src/view/screens/Feeds.tsx:128 +msgid "My Feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:454 +msgid "My Saved Feeds" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:162 +#: src/view/com/modals/CreateOrEditList.tsx:202 +msgid "Name" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:76 +msgid "Never lose access to your followers and data." +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:203 +#: src/view/screens/Feeds.tsx:261 +#: src/view/screens/Profile.tsx:304 +#: src/view/screens/ProfileFeed.tsx:378 +#: src/view/screens/ProfileList.tsx:210 +#: src/view/screens/ProfileList.tsx:248 +#: src/view/shell/desktop/LeftNav.tsx:229 +msgid "New post" +msgstr "" + +#: src/view/shell/desktop/LeftNav.tsx:239 +msgid "New Post" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:105 +#: src/view/com/auth/login/LoginForm.tsx:279 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:158 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:168 +msgid "Next" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:137 +msgid "Next image" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:472 +#: src/view/screens/ProfileList.tsx:667 +msgid "No description" +msgstr "" + +#: src/view/screens/Feeds.tsx:207 +msgid "No results found for \"{query}\"" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:140 +#: src/view/shell/desktop/Search.tsx:112 +msgid "No results found for {0}" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:136 +msgid "Not Applicable" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:178 +#: src/view/shell/Drawer.tsx:262 +msgid "Notifications" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:41 +msgid "Okay" +msgstr "" + +#: src/view/com/composer/Composer.tsx:334 +msgid "One or more images is missing alt text." +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:50 +msgid "Open navigation" +msgstr "" + +#: src/view/screens/Settings.tsx:463 +msgid "Opens configurable language settings" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:284 +msgid "Opens modal for using custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:482 +msgid "Opens moderation settings" +msgstr "" + +#: src/view/screens/Settings.tsx:448 +msgid "Opens screen with all saved feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:501 +msgid "Opens the app password settings page" +msgstr "" + +#: src/view/screens/Settings.tsx:415 +msgid "Opens the home feed preferences" +msgstr "" + +#: src/view/screens/Settings.tsx:580 +msgid "Opens the storybook page" +msgstr "" + +#: src/view/screens/Settings.tsx:560 +msgid "Opens the system log page" +msgstr "" + +#: src/view/screens/Settings.tsx:432 +msgid "Opens the threads preferences" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:98 +msgid "Other account" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:88 +msgid "Other service" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:84 +msgid "Other..." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:100 +#: src/view/com/auth/create/Step2.tsx:110 +#: src/view/com/auth/login/LoginForm.tsx:214 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:132 +#: src/view/com/modals/DeleteAccount.tsx:183 +msgid "Password" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:28 +msgid "Password updated!" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:122 +msgid "Pictures meant for adults." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:67 +msgid "Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:125 +msgid "Please enter a unique name for this App Password or use our randomly generated one." +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:172 +msgid "Please enter your password as well:" +msgstr "" + +#: src/view/com/composer/Composer.tsx:317 +msgid "Post" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:343 +msgid "Post hidden" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:80 +msgid "Post language" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:69 +msgid "Post Languages" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:309 +msgid "Post not found" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:51 +msgid "Potentially Misleading Link" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:123 +msgid "Previous image" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:73 +msgid "Prioritize Your Follows" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:220 +#: src/view/shell/Drawer.tsx:161 +#: src/view/shell/Drawer.tsx:325 +msgid "Profile" +msgstr "" + +#: src/view/screens/Settings.tsx:689 +msgid "Protect your account by verifying your email." +msgstr "" + +#: src/view/com/modals/Repost.tsx:53 +msgid "Quote post" +msgstr "" + +#: src/view/com/modals/Repost.tsx:57 +msgid "Quote Post" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:236 +msgid "Ratios" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:84 +msgid "Remove" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:154 +msgid "Remove image" +msgstr "" + +#: src/view/com/composer/ExternalEmbed.tsx:70 +msgid "Remove image preview" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:120 +msgid "Reply Filters" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:165 +msgid "Report {collectionName}" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:37 +msgid "Report post" +msgstr "" + +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:69 +msgid "Repost or quote post" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:181 +#: src/view/com/modals/ChangeEmail.tsx:183 +msgid "Request Change" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:52 +msgid "Required for this provider" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:110 +msgid "Reset code" +msgstr "" + +#: src/view/screens/Settings.tsx:602 +msgid "Reset onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:592 +msgid "Reset preferences state" +msgstr "" + +#: src/view/screens/Settings.tsx:600 +msgid "Resets the onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:590 +msgid "Resets the preferences state" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:114 +#: src/view/com/auth/create/CreateAccount.tsx:118 +#: src/view/com/auth/login/LoginForm.tsx:256 +#: src/view/com/auth/login/LoginForm.tsx:259 +#: src/view/com/util/error/ErrorMessage.tsx:55 +#: src/view/com/util/error/ErrorScreen.tsx:65 +msgid "Retry" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:166 +msgid "Retry change handle" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:114 +#: src/view/com/modals/BirthDateSettings.tsx:90 +#: src/view/com/modals/BirthDateSettings.tsx:93 +#: src/view/com/modals/ChangeHandle.tsx:179 +#: src/view/com/modals/CreateOrEditList.tsx:240 +#: src/view/com/modals/CreateOrEditList.tsx:248 +#: src/view/com/modals/EditProfile.tsx:225 +msgid "Save" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:105 +msgid "Save alt text" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:210 +msgid "Save changes" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:233 +msgid "Save Changes" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:176 +msgid "Save handle change" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:144 +msgid "Save image crop" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:89 +#: src/view/com/search/HeaderWithInput.tsx:101 +#: src/view/com/util/forms/SearchInput.tsx:64 +#: src/view/shell/bottom-bar/BottomBar.tsx:132 +#: src/view/shell/desktop/Search.tsx:81 +#: src/view/shell/Drawer.tsx:218 +msgid "Search" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:110 +msgid "Security Step Required" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:30 +msgid "See what's next" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:75 +msgid "Select Bluesky Social" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:115 +msgid "Select from an existing account" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:141 +msgid "Select service" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:191 +msgid "Send Confirmation Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:119 +msgid "Send email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:130 +msgid "Send Email" +msgstr "" + +#: src/view/shell/Drawer.tsx:349 +#: src/view/shell/Drawer.tsx:370 +msgid "Send feedback" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:45 +msgid "Send Report" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:80 +msgid "Set new password" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:172 +msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:101 +msgid "Set this setting to \"No\" to hide all replies from your feed." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:155 +msgid "Set this setting to \"No\" to hide all reposts from your feed." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:96 +msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:191 +msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." +msgstr "" + +#: src/view/shell/Drawer.tsx:338 +msgid "Settings" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:126 +msgid "Sexual activity or erotic nudity." +msgstr "" + +#: src/view/screens/Settings.tsx:235 +msgid "Show" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:114 +msgid "Show anyway" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:188 +msgid "Show Posts from My Feeds" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:169 +msgid "Show Quote Posts" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:98 +msgid "Show Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:76 +msgid "Show replies by people you follow before all other replies." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:152 +msgid "Show Reposts" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:337 +msgid "Show users" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:95 +#: src/view/com/auth/SplashScreen.tsx:50 +msgid "Sign in" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:53 +#: src/view/com/auth/SplashScreen.web.tsx:59 +msgid "Sign In" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:65 +msgid "Sign in as {0}" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:56 +#: src/view/com/auth/login/Login.tsx:114 +msgid "Sign in as..." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:128 +msgid "Sign into" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:71 +#: src/view/com/modals/SwitchAccount.tsx:74 +#: src/view/screens/Settings.tsx:274 +#: src/view/screens/Settings.tsx:277 +msgid "Sign out" +msgstr "" + +#: src/view/screens/Settings.tsx:245 +msgid "Signed in as" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:37 +msgid "Skip" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:51 +msgid "Sort Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:54 +msgid "Sort replies to the same post by:" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:122 +msgid "Square" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:106 +#: src/view/com/modals/ServerInput.tsx:62 +msgid "Staging" +msgstr "" + +#: src/view/screens/Settings.tsx:624 +msgid "Status page" +msgstr "" + +#: src/view/screens/Settings.tsx:582 +msgid "Storybook" +msgstr "" + +#: src/view/screens/ProfileList.tsx:533 +msgid "Subscribe" +msgstr "" + +#: src/view/screens/ProfileList.tsx:529 +msgid "Subscribe to this list" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:47 +msgid "Switch Account" +msgstr "" + +#: src/view/screens/Settings.tsx:562 +msgid "System log" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:112 +msgid "Tall" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:50 +msgid "Text input field" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:312 +msgid "The post may have been deleted." +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:71 +msgid "This {screenDescription} has been flagged:" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:58 +msgid "This information is not shared with other users." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:107 +msgid "This is important in case you ever need to change your email or reset your password." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:71 +msgid "This is the service that keeps you online." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:63 +msgid "This link is taking you to the following website:" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:176 +msgid "This post has been deleted." +msgstr "" + +#: src/view/screens/Settings.tsx:441 +msgid "Thread Preferences" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:93 +msgid "Threaded Mode" +msgstr "" + +#: src/view/com/util/forms/DropdownButton.tsx:230 +msgid "Toggle dropdown" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:271 +msgid "Transformations" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:654 +msgid "Translate" +msgstr "" + +#: src/view/com/util/error/ErrorScreen.tsx:73 +msgid "Try again" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:74 +#: src/view/com/auth/login/LoginForm.tsx:113 +msgid "Unable to contact your service. Please check your Internet connection." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:386 +#: src/view/com/profile/ProfileHeader.tsx:389 +msgid "Unblock" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:185 +msgid "Update {displayName} in Lists" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:174 +msgid "Updating..." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:456 +msgid "Upload a text file to:" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:148 +msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:516 +msgid "Use default provider" +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:37 +msgid "User handle" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:168 +#: src/view/com/auth/login/LoginForm.tsx:185 +msgid "Username or email address" +msgstr "" + +#: src/view/screens/ProfileList.tsx:694 +msgid "Users" +msgstr "" + +#: src/view/screens/Settings.tsx:650 +msgid "Verify email" +msgstr "" + +#: src/view/screens/Settings.tsx:675 +msgid "Verify my email" +msgstr "" + +#: src/view/screens/Settings.tsx:684 +msgid "Verify My Email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:205 +#: src/view/com/modals/ChangeEmail.tsx:207 +msgid "Verify New Email" +msgstr "" + +#: src/view/screens/Log.tsx:53 +msgid "View debug entry" +msgstr "" + +#: src/view/com/profile/ProfileSubpageHeader.tsx:127 +msgid "View the avatar" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:80 +msgid "Visit Site" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:78 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:168 +msgid "What is the issue with this {collectionName}?" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:72 +msgid "Which languages are used in this post?" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:71 +msgid "Which languages would you like to see in your algorithmic feeds?" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:102 +msgid "Wide" +msgstr "" + +#: src/view/com/composer/Composer.tsx:390 +msgid "Write post" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:31 +msgid "Write your reply" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:122 +msgid "You can change hosting providers at any time." +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:31 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:346 +msgid "You have blocked the author or you have been blocked by the author." +msgstr "" + +#: src/view/com/lists/ListsList.tsx:112 +msgid "You have no lists." +msgstr "" + +#: src/view/screens/AppPasswords.tsx:55 +msgid "You have not created any app passwords yet. You can create one by pressing the button below." +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:83 +msgid "You will receive an email with a \"reset code.\" Enter that code here, then enter your new password." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:42 +msgid "Your account" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:121 +msgid "Your birth date" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:107 +msgid "Your email has been saved! We'll be in touch soon." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:125 +msgid "Your email has been updated but not verified. As a next step, please verify your new email." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:102 +msgid "Your email has not yet been verified. This is an important security step which we recommend." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:41 +#: src/view/com/modals/ChangeHandle.tsx:275 +msgid "Your full handle will be" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:69 +msgid "Your hosting provider" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:63 +msgid "Your posts, likes, and blocks are public. Mutes are private." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:27 +msgid "Your user handle" +msgstr "" diff --git a/src/locale/locales/fr/messages.js b/src/locale/locales/fr/messages.js new file mode 100644 index 000000000..8d85ab77c --- /dev/null +++ b/src/locale/locales/fr/messages.js @@ -0,0 +1 @@ +/*eslint-disable*/module.exports={messages:JSON.parse("{\"PBodTo\":\"- end of feed -\",\"EtUMsZ\":\". This warning is only available for posts with media attached.\",\"J/hVSQ\":[[\"0\"]],\"hZQerY\":[[\"0\"],\" \",[\"purposeLabel\"],\" List\"],\"6RmyWt\":\"<0>Here is your app password.</0> Use this to sign into the other app along with your handle.\",\"AnNF5e\":\"Accessibility\",\"AeXO77\":\"Account\",\"4WY4MD\":\"Account options\",\"m16xKo\":\"Add\",\"fBBX+K\":\"Add a content warning\",\"JU3hs2\":\"Add a user to this list\",\"MPPZ54\":\"Add account\",\"LkA8jz\":\"Add alt text\",\"Z8idyM\":\"Add details\",\"AoXl11\":\"Add details to report\",\"iE6B/9\":\"Add link card\",\"EXHdP1\":\"Add link card:\",\"x6laaL\":\"Add the following DNS record to your domain:\",\"jRrQFe\":\"Adjust the number of likes a reply must have to be shown in your feed.\",\"qLa52r\":\"Adult Content\",\"sxkWRg\":\"Advanced\",\"u2HO/d\":\"ALT\",\"u/DP73\":\"Alt text\",\"0QlT7/\":\"Alt text describes images for blind and low-vision users, and helps give context to everyone.\",\"woXbjq\":[\"An email has been sent to \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"Fon2dK\":[\"An email has been sent to your previous address, \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"HZFm5R\":\"and\",\"SMmUnj\":\"App passwords\",\"aAIQg2\":\"Appearance\",\"EbvWd3\":\"Artistic or non-erotic nudity.\",\"iH8pgl\":\"Back\",\"ehOkF+\":\"Basics\",\"+gCI2a\":\"Birthday\",\"pieVBA\":\"Birthday:\",\"HFCE4A\":\"Blocked post.\",\"7A9u1j\":\"Bluesky\",\"ZHmKSm\":\"Bluesky is flexible.\",\"odLrdl\":\"Bluesky is open.\",\"/LsWK4\":\"Bluesky is public.\",\"C50OGr\":\"Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon.\",\"klVoaP\":\"Bluesky.Social\",\"qxBitM\":[\"Build version \",[\"0\"],\" \",[\"1\"]],\"rT2cV+\":\"Camera\",\"JGGrPC\":\"Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long.\",\"dEgA5A\":\"Cancel\",\"aMH9rr\":\"Cancel account deletion\",\"kc3E4R\":\"Cancel add image alt text\",\"wg4LHQ\":\"Cancel change handle\",\"hFL1Li\":\"Cancel image crop\",\"tijH8t\":\"Cancel profile editing\",\"Qe4C/d\":\"Cancel quote post\",\"5TviPn\":\"Cancel search\",\"nss3UV\":\"Cancel waitlist signup\",\"o+XJ9D\":\"Change\",\"pQco5R\":\"Change handle\",\"Q5e1U/\":\"Change Handle\",\"a3NAfL\":\"Change my email\",\"4vatyk\":\"Change Your Email\",\"/+X+/K\":\"Check your inbox for an email with the confirmation code to enter below:\",\"Rt502e\":\"Choose Service\",\"/L45sc\":\"Choose the algorithms that power your experience with custom feeds.\",\"Wk8hkn\":\"Choose your password\",\"QbvBNV\":\"Clear search query\",\"flH7u/\":\"Close alert\",\"hYmnbk\":\"Close bottom drawer\",\"47L1V1\":\"Close image\",\"l49ujN\":\"Close image viewer\",\"UryHFO\":\"Close navigation footer\",\"o8UUti\":\"Compose reply\",\"7VpPHA\":\"Confirm\",\"q8upsf\":\"Confirm Change\",\"8pNKIr\":\"Confirm content language settings\",\"tGg8Kt\":\"Confirm delete account\",\"ioZOzk\":\"Confirmation code\",\"J28zul\":\"Connecting...\",\"m8j6up\":\"Content Filtering\",\"/PlAsF\":\"Content Languages\",\"cogwXi\":\"Content Warning\",\"FG7AQv\":\"Content warnings\",\"6V3Ea3\":\"Copied\",\"he3ygx\":\"Copy\",\"7wWvgo\":\"Could not load feed\",\"8NNr/O\":\"Could not load list\",\"mpt9T+\":\"Create a new account\",\"IS0nrP\":\"Create Account\",\"6HbhpU\":\"Create new account\",\"GAD3Dx\":\"Custom domain\",\"ZQKLI1\":\"Danger Zone\",\"ZDGm40\":\"Delete account\",\"vzX5FB\":\"Delete Account\",\"gUEtxf\":\"Delete app password\",\"ktknoE\":\"Delete my account\",\"szz0+N\":\"Delete my account…\",\"u+1OHY\":\"Deleted post.\",\"Nu4oKW\":\"Description\",\"dacKHE\":\"Dev Server\",\"2ygkE8\":\"Developer Tools\",\"pbLwal\":\"Discover new feeds\",\"pfa8F0\":\"Display name\",\"0gS7M5\":\"Display Name\",\"iZ5pMB\":\"Domain verified!\",\"DPfwMq\":\"Done\",\"zT97vP\":[\"Done\",[\"extraText\"]],\"XQFMOm\":\"Edit image\",\"cLmurE\":\"Edit My Feeds\",\"bRZ5XW\":\"Edit my profile\",\"9OpVZg\":\"Edit profile\",\"QJQd1J\":\"Edit Profile\",\"Jn7kox\":\"Edit Saved Feeds\",\"O3oNi5\":\"Email\",\"ATGYL1\":\"Email address\",\"pJJ0Vp\":\"Email Updated\",\"96mted\":\"Enable this setting to only see replies between people you follow.\",\"YbIxza\":\"Enter the address of your provider:\",\"BfIgP6\":\"Enter the domain you want to use\",\"xRPn3U\":\"Enter your email address\",\"+inPGm\":\"Enter your new email address below.\",\"T0KLp4\":\"Enter your username and password\",\"0PkE20\":\"Expand alt text\",\"4yCy8i\":\"Feed offline\",\"N0CqyO\":\"Feed Preferences\",\"YirHq7\":\"Feedback\",\"2DoBvq\":\"Feeds\",\"Qzj1WT\":\"Finding similar accounts...\",\"QKSrQV\":\"Fine-tune the content you see on your home screen.\",\"r+KeyR\":\"Fine-tune the discussion threads.\",\"MKEPCY\":\"Follow\",\"NIjL2Y\":\"following\",\"y6sq5j\":\"Following\",\"p3UO/y\":\"Follows you\",\"5RhDkD\":\"For security reasons, we'll need to send a confirmation code to your email address.\",\"NJPhAO\":\"For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one.\",\"5bDfuq\":\"Forgot\",\"hEPLrs\":\"Forgot password\",\"dn8X5t\":\"Forgot Password\",\"U+kFad\":\"Gallery\",\"c3b0B0\":\"Get Started\",\"CKyk7Q\":\"Go back\",\"sr0UJD\":\"Go Back\",\"Rtp0y7\":\"Go to next\",\"Nf7oXL\":\"Handle\",\"c3XJ18\":\"Help\",\"vLyv1R\":\"Hide\",\"qdOx2q\":\"Hide user list\",\"i0qMbr\":\"Home\",\"sXZ8IU\":\"Home Feed Preferences\",\"yt7fhu\":\"Hosting provider\",\"s2xA6t\":\"Hosting provider address\",\"o+axy6\":\"I have a code\",\"wey2os\":\"I have my own domain\",\"WlEcKr\":\"If none are selected, suitable for all ages.\",\"VCk0rR\":\"Image alt text\",\"STGpNQ\":\"Image options\",\"dSKHAa\":\"Invalid username or password\",\"MFKlMB\":\"Invite\",\"F5MZVk\":\"Invite a Friend\",\"6KlkHI\":\"Invite code\",\"F75w8j\":\"Join the waitlist\",\"6iVTdm\":\"Join the waitlist.\",\"SNzppu\":\"Join Waitlist\",\"Dcq5kL\":\"Language selection\",\"GAmD3h\":\"Languages\",\"NgeSlx\":\"Learn More\",\"rj0Lke\":\"Learn more about this warning\",\"kq2ga7\":\"Leave them all unchecked to see any language.\",\"QfDITI\":\"Leaving Bluesky\",\"Esfg1M\":\"Let's get your password reset!\",\"BvSY1i\":\"Like this feed\",\"FuZWua\":\"List Avatar\",\"8mjA4F\":\"List Name\",\"h16FyT\":\"Lists\",\"ujW4FW\":\"Load more posts\",\"VkLESX\":\"Load new posts\",\"jl0AFf\":\"Local dev server\",\"cR9UpQ\":\"Login to account that is not listed\",\"2U/gDT\":\"Make sure this is where you intend to go!\",\"zucql+\":\"Menu\",\"DzmsLV\":\"Moderation\",\"3Siwmw\":\"More options\",\"Y17r45\":\"More post options\",\"Mysqyf\":\"My Birthday\",\"6MBNS/\":\"My Feeds\",\"Ha6iBv\":\"My Saved Feeds\",\"6YtxFj\":\"Name\",\"8yolS6\":\"Never lose access to your followers and data.\",\"2B7HLH\":\"New post\",\"FGrimz\":\"New Post\",\"hXzOVo\":\"Next\",\"EatZYJ\":\"Next image\",\"flmDTf\":\"No description\",\"fOlAiK\":[\"No results found for \\\"\",[\"query\"],\"\\\"\"],\"kA9DpB\":[\"No results found for \",[\"0\"]],\"ispbnl\":\"Not Applicable\",\"iDNBZe\":\"Notifications\",\"UaXeX3\":\"Okay\",\"Cqo2D+\":\"One or more images is missing alt text.\",\"M/Q2aG\":\"Open navigation\",\"M5PuNq\":\"Opens configurable language settings\",\"eSqpax\":\"Opens modal for using custom domain\",\"vYwHHI\":\"Opens moderation settings\",\"0tHyB7\":\"Opens screen with all saved feeds\",\"nmRoY/\":\"Opens the app password settings page\",\"6e9Apv\":\"Opens the home feed preferences\",\"O87Dr/\":\"Opens the storybook page\",\"G+PVmg\":\"Opens the system log page\",\"Jqb7sy\":\"Opens the threads preferences\",\"b22AVl\":\"Other account\",\"n+HLOP\":\"Other service\",\"1PKxQ7\":\"Other...\",\"8ZsakT\":\"Password\",\"DKeVgZ\":\"Password updated!\",\"VeZE5Q\":\"Pictures meant for adults.\",\"Apyknf\":\"Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed.\",\"9qpQ5O\":\"Please enter a unique name for this App Password or use our randomly generated one.\",\"QJr5Xp\":\"Please enter your password as well:\",\"y28hnO\":\"Post\",\"h5RcXU\":\"Post hidden\",\"r5zLS0\":\"Post language\",\"AzCucI\":\"Post Languages\",\"tJFPmV\":\"Post not found\",\"0+DQbr\":\"Potentially Misleading Link\",\"MHk+7g\":\"Previous image\",\"x8iR7V\":\"Prioritize Your Follows\",\"vERlcd\":\"Profile\",\"MrgqOW\":\"Protect your account by verifying your email.\",\"8HFFRQ\":\"Quote post\",\"+KrAHa\":\"Quote Post\",\"WlWsdE\":\"Ratios\",\"t/YqKh\":\"Remove\",\"5ywtDz\":\"Remove image\",\"Dw/XUh\":\"Remove image preview\",\"Veu9gK\":\"Reply Filters\",\"lQWbAr\":[\"Report \",[\"collectionName\"]],\"6IcSvC\":\"Report post\",\"JOV5dR\":\"Repost or quote post\",\"bqG37Z\":\"Request Change\",\"8XIT+P\":\"Required for this provider\",\"vJgYMA\":\"Reset code\",\"xEL92I\":\"Reset onboarding state\",\"bee/Fw\":\"Reset preferences state\",\"wToeoz\":\"Resets the onboarding state\",\"nIU7qI\":\"Resets the preferences state\",\"6gRgw8\":\"Retry\",\"hAbYQa\":\"Retry change handle\",\"tfDRzk\":\"Save\",\"KV2YQQ\":\"Save alt text\",\"y3aU20\":\"Save changes\",\"IUwGEM\":\"Save Changes\",\"Xs07Tg\":\"Save handle change\",\"BckA7m\":\"Save image crop\",\"A1taO8\":\"Search\",\"CKROFy\":\"Security Step Required\",\"cNzyJW\":\"See what's next\",\"L5sM7N\":\"Select Bluesky Social\",\"o3dwub\":\"Select from an existing account\",\"GGw2AK\":\"Select service\",\"vp9yIB\":\"Send Confirmation Email\",\"65dxv8\":\"Send email\",\"i/TzEU\":\"Send Email\",\"RoafuO\":\"Send feedback\",\"4cijjm\":\"Send Report\",\"V/e7nf\":\"Set new password\",\"gwsie4\":\"Set this setting to \\\"No\\\" to hide all quote posts from your feed. Reposts will still be visible.\",\"IZjC3J\":\"Set this setting to \\\"No\\\" to hide all replies from your feed.\",\"KIgU3l\":\"Set this setting to \\\"No\\\" to hide all reposts from your feed.\",\"zaAyrz\":\"Set this setting to \\\"Yes\\\" to show replies in a threaded view. This is an experimental feature.\",\"fQV2eE\":\"Set this setting to \\\"Yes\\\" to show samples of your saved feeds in your following feed. This is an experimental feature.\",\"Tz0i8g\":\"Settings\",\"HfWHhJ\":\"Sexual activity or erotic nudity.\",\"8vETh9\":\"Show\",\"aWAdCb\":\"Show anyway\",\"NijgXr\":\"Show Posts from My Feeds\",\"T3Mt8m\":\"Show Quote Posts\",\"BlW8X/\":\"Show Replies\",\"X4GwDb\":\"Show replies by people you follow before all other replies.\",\"GiogzH\":\"Show Reposts\",\"fhY/fL\":\"Show users\",\"5lWFkC\":\"Sign in\",\"n1ekoW\":\"Sign In\",\"N9o7n5\":[\"Sign in as \",[\"0\"]],\"FT1MVS\":\"Sign in as...\",\"+UpfFC\":\"Sign into\",\"fcWrnU\":\"Sign out\",\"zU+Ro7\":\"Signed in as\",\"6Uau97\":\"Skip\",\"0o5BFH\":\"Sort Replies\",\"GH1Rgk\":\"Sort replies to the same post by:\",\"1DA6ap\":\"Square\",\"aKEHLj\":\"Staging\",\"tgEXwM\":\"Status page\",\"P5jja7\":\"Storybook\",\"EDl9kS\":\"Subscribe\",\"5z3ICN\":\"Subscribe to this list\",\"VjWeLI\":\"Switch Account\",\"fP8jTZ\":\"System log\",\"HF6Iah\":\"Tall\",\"p8Iz39\":\"Text input field\",\"G4EksE\":\"The post may have been deleted.\",\"KRYn8w\":[\"This \",[\"screenDescription\"],\" has been flagged:\"],\"lm845B\":\"This information is not shared with other users.\",\"5Pvw/O\":\"This is important in case you ever need to change your email or reset your password.\",\"sQQfZ9\":\"This is the service that keeps you online.\",\"CvX8qs\":\"This link is taking you to the following website:\",\"WKrUVy\":\"This post has been deleted.\",\"u9ThjD\":\"Thread Preferences\",\"zmXsk5\":\"Threaded Mode\",\"1x30Qt\":\"Toggle dropdown\",\"KFXQEt\":\"Transformations\",\"pi8x/S\":\"Translate\",\"KDw4GX\":\"Try again\",\"nc4Wfd\":\"Unable to contact your service. Please check your Internet connection.\",\"tuS5Jz\":\"Unblock\",\"vaz2uI\":[\"Update \",[\"displayName\"],\" in Lists\"],\"RXbEvi\":\"Updating...\",\"Vwkfp4\":\"Upload a text file to:\",\"jTdnU6\":\"Use app passwords to login to other Bluesky clients without giving full access to your account or password.\",\"CH1am9\":\"Use default provider\",\"t4Yp4Z\":\"User handle\",\"nZx9mr\":\"Username or email address\",\"Sxm8rQ\":\"Users\",\"MBOY4U\":\"Verify email\",\"Ejyv0o\":\"Verify my email\",\"9czCrB\":\"Verify My Email\",\"ibSVGR\":\"Verify New Email\",\"nHsQde\":\"View debug entry\",\"47jzzd\":\"View the avatar\",\"wK4H1r\":\"Visit Site\",\"qjBGxf\":\"We're so excited to have you join us!\",\"Mj7rl/\":[\"What is the issue with this \",[\"collectionName\"],\"?\"],\"3qn29J\":\"Which languages are used in this post?\",\"uawiGa\":\"Which languages would you like to see in your algorithmic feeds?\",\"I5S9ZE\":\"Wide\",\"y02THm\":\"Write post\",\"6ckZRB\":\"Write your reply\",\"STPj0e\":\"You can change hosting providers at any time.\",\"67nRLM\":\"You can now sign in with your new password.\",\"RkXibf\":\"You have blocked the author or you have been blocked by the author.\",\"tCLJ9E\":\"You have no lists.\",\"NDgp3i\":\"You have not created any app passwords yet. You can create one by pressing the button below.\",\"RrDyEb\":\"You will receive an email with a \\\"reset code.\\\" Enter that code here, then enter your new password.\",\"gdRnT7\":\"Your account\",\"k7hmsH\":\"Your birth date\",\"OubkcP\":\"Your email has been saved! We'll be in touch soon.\",\"z2L+/9\":\"Your email has been updated but not verified. As a next step, please verify your new email.\",\"XZlIVw\":\"Your email has not yet been verified. This is an important security step which we recommend.\",\"qv9f4I\":\"Your full handle will be\",\"lvcqqG\":\"Your hosting provider\",\"Oqt/PG\":\"Your posts, likes, and blocks are public. Mutes are private.\",\"MvWO9d\":\"Your user handle\"}")}; \ No newline at end of file diff --git a/src/locale/locales/fr/messages.po b/src/locale/locales/fr/messages.po new file mode 100644 index 000000000..c7c8b3866 --- /dev/null +++ b/src/locale/locales/fr/messages.po @@ -0,0 +1,1544 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-11-05 16:01-0800\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: fr\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/view/screens/Profile.tsx:212 +msgid "- end of feed -" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:138 +msgid ". This warning is only available for posts with media attached." +msgstr "" + +#: src/view/com/modals/Repost.tsx:45 +msgid "{0}" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:167 +msgid "{0} {purposeLabel} List" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:132 +msgid "<0>Here is your app password.</0> Use this to sign into the other app along with your handle." +msgstr "" + +#: src/view/com/modals/EditImage.tsx:299 +#: src/view/screens/Settings.tsx:363 +msgid "Accessibility" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:157 +#: src/view/screens/Settings.tsx:201 +msgid "Account" +msgstr "" + +#: src/view/com/util/AccountDropdownBtn.tsx:41 +msgid "Account options" +msgstr "" + +#: src/view/screens/ProfileList.tsx:710 +msgid "Add" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:57 +msgid "Add a content warning" +msgstr "" + +#: src/view/screens/ProfileList.tsx:700 +msgid "Add a user to this list" +msgstr "" + +#: src/view/screens/Settings.tsx:313 +#: src/view/screens/Settings.tsx:322 +msgid "Add account" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:119 +#: src/view/com/composer/photos/Gallery.tsx:167 +msgid "Add alt text" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:41 +#: src/view/com/modals/report/Modal.tsx:190 +msgid "Add details" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:193 +msgid "Add details to report" +msgstr "" + +#: src/view/com/composer/Composer.tsx:419 +msgid "Add link card" +msgstr "" + +#: src/view/com/composer/Composer.tsx:422 +msgid "Add link card:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:418 +msgid "Add the following DNS record to your domain:" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:140 +msgid "Adjust the number of likes a reply must have to be shown in your feed." +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:76 +msgid "Adult Content" +msgstr "" + +#: src/view/screens/Settings.tsx:493 +msgid "Advanced" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:127 +msgid "ALT" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:315 +msgid "Alt text" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:193 +msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:112 +msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:119 +msgid "An email has been sent to your previous address, {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:238 +msgid "and" +msgstr "" + +#: src/view/screens/Settings.tsx:509 +msgid "App passwords" +msgstr "" + +#: src/view/screens/Settings.tsx:378 +msgid "Appearance" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:124 +msgid "Artistic or non-erotic nudity." +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:92 +#: src/view/com/auth/login/ChooseAccountForm.tsx:111 +#: src/view/com/auth/login/LoginForm.tsx:247 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:150 +#: src/view/com/modals/report/InputIssueDetails.tsx:45 +#: src/view/com/post-thread/PostThread.tsx:317 +#: src/view/com/post-thread/PostThread.tsx:325 +#: src/view/com/post-thread/PostThread.tsx:354 +#: src/view/com/post-thread/PostThread.tsx:362 +#: src/view/com/profile/ProfileHeader.tsx:576 +msgid "Back" +msgstr "" + +#: src/view/screens/Settings.tsx:407 +msgid "Basics" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:130 +#: src/view/com/modals/BirthDateSettings.tsx:69 +msgid "Birthday" +msgstr "" + +#: src/view/screens/Settings.tsx:228 +msgid "Birthday:" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:211 +msgid "Blocked post." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:52 +#: src/view/com/auth/SplashScreen.tsx:27 +msgid "Bluesky" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:84 +msgid "Bluesky is flexible." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:73 +msgid "Bluesky is open." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:60 +msgid "Bluesky is public." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:70 +msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon." +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:78 +msgid "Bluesky.Social" +msgstr "" + +#: src/view/screens/Settings.tsx:612 +msgid "Build version {0} {1}" +msgstr "" + +#: src/view/com/composer/photos/OpenCameraBtn.tsx:62 +msgid "Camera" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:199 +msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long." +msgstr "" + +#: src/view/com/composer/Composer.tsx:271 +#: src/view/com/composer/Composer.tsx:274 +#: src/view/com/modals/AltImage.tsx:127 +#: src/view/com/modals/ChangeEmail.tsx:216 +#: src/view/com/modals/ChangeEmail.tsx:218 +#: src/view/com/modals/Confirm.tsx:89 +#: src/view/com/modals/CreateOrEditList.tsx:258 +#: src/view/com/modals/CreateOrEditList.tsx:263 +#: src/view/com/modals/DeleteAccount.tsx:142 +#: src/view/com/modals/DeleteAccount.tsx:215 +#: src/view/com/modals/EditImage.tsx:323 +#: src/view/com/modals/EditProfile.tsx:250 +#: src/view/com/modals/LinkWarning.tsx:90 +#: src/view/com/modals/Repost.tsx:74 +#: src/view/com/modals/UserAddRemoveLists.tsx:199 +#: src/view/com/modals/Waitlist.tsx:136 +#: src/view/com/search/HeaderWithInput.tsx:127 +#: src/view/shell/desktop/Search.tsx:93 +msgid "Cancel" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:138 +#: src/view/com/modals/DeleteAccount.tsx:211 +msgid "Cancel account deletion" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:122 +msgid "Cancel add image alt text" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:144 +msgid "Cancel change handle" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:134 +msgid "Cancel image crop" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:245 +msgid "Cancel profile editing" +msgstr "" + +#: src/view/com/modals/Repost.tsx:65 +msgid "Cancel quote post" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:101 +#: src/view/shell/desktop/Search.tsx:89 +msgid "Cancel search" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:132 +msgid "Cancel waitlist signup" +msgstr "" + +#: src/view/screens/Settings.tsx:222 +msgid "Change" +msgstr "" + +#: src/view/screens/Settings.tsx:517 +#: src/view/screens/Settings.tsx:526 +msgid "Change handle" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:156 +msgid "Change Handle" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:136 +msgid "Change my email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:109 +msgid "Change Your Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:155 +msgid "Check your inbox for an email with the confirmation code to enter below:" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:38 +msgid "Choose Service" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:87 +msgid "Choose the algorithms that power your experience with custom feeds." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:105 +msgid "Choose your password" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:111 +#: src/view/com/util/forms/SearchInput.tsx:73 +msgid "Clear search query" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:38 +msgid "Close alert" +msgstr "" + +#: src/view/com/util/BottomSheetCustomBackdrop.tsx:33 +msgid "Close bottom drawer" +msgstr "" + +#: src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx:26 +msgid "Close image" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:107 +msgid "Close image viewer" +msgstr "" + +#: src/view/shell/index.web.tsx:65 +msgid "Close navigation footer" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:22 +msgid "Compose reply" +msgstr "" + +#: src/view/com/modals/Confirm.tsx:76 +#: src/view/com/modals/SelfLabel.tsx:156 +#: src/view/com/modals/VerifyEmail.tsx:220 +#: src/view/screens/PreferencesHomeFeed.tsx:223 +#: src/view/screens/PreferencesThreads.tsx:128 +msgid "Confirm" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:193 +#: src/view/com/modals/ChangeEmail.tsx:195 +msgid "Confirm Change" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:34 +msgid "Confirm content language settings" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:201 +msgid "Confirm delete account" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:157 +#: src/view/com/modals/DeleteAccount.tsx:168 +#: src/view/com/modals/VerifyEmail.tsx:154 +msgid "Confirmation code" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:125 +#: src/view/com/auth/login/LoginForm.tsx:266 +msgid "Connecting..." +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:42 +msgid "Content Filtering" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:68 +msgid "Content Languages" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:68 +msgid "Content Warning" +msgstr "" + +#: src/view/com/composer/labels/LabelsBtn.tsx:32 +msgid "Content warnings" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:178 +#: src/view/com/modals/InviteCodes.tsx:129 +msgid "Copied" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:171 +msgid "Copy" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:97 +msgid "Could not load feed" +msgstr "" + +#: src/view/screens/ProfileList.tsx:788 +msgid "Could not load list" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:42 +msgid "Create a new account" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:77 +msgid "Create Account" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:39 +msgid "Create new account" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:390 +#: src/view/com/modals/ServerInput.tsx:102 +msgid "Custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:531 +msgid "Danger Zone" +msgstr "" + +#: src/view/screens/Settings.tsx:538 +msgid "Delete account" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:75 +msgid "Delete Account" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:193 +msgid "Delete app password" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:204 +msgid "Delete my account" +msgstr "" + +#: src/view/screens/Settings.tsx:548 +msgid "Delete my account…" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:203 +msgid "Deleted post." +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:209 +#: src/view/com/modals/CreateOrEditList.tsx:225 +#: src/view/com/modals/EditProfile.tsx:199 +#: src/view/com/modals/EditProfile.tsx:211 +msgid "Description" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:112 +msgid "Dev Server" +msgstr "" + +#: src/view/screens/Settings.tsx:553 +msgid "Developer Tools" +msgstr "" + +#: src/view/screens/Feeds.tsx:165 +msgid "Discover new feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:193 +msgid "Display name" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:181 +msgid "Display Name" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:488 +msgid "Domain verified!" +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:77 +#: src/view/com/modals/ContentFilteringSettings.tsx:85 +#: src/view/com/modals/crop-image/CropImage.web.tsx:152 +#: src/view/com/modals/EditImage.tsx:333 +#: src/view/com/modals/ListAddUser.tsx:153 +#: src/view/com/modals/SelfLabel.tsx:159 +#: src/view/screens/PreferencesHomeFeed.tsx:226 +#: src/view/screens/PreferencesThreads.tsx:131 +msgid "Done" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:42 +msgid "Done{extraText}" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:141 +#: src/view/com/modals/EditImage.tsx:207 +msgid "Edit image" +msgstr "" + +#: src/view/screens/Feeds.tsx:132 +msgid "Edit My Feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:158 +msgid "Edit my profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:373 +msgid "Edit profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:376 +msgid "Edit Profile" +msgstr "" + +#: src/view/screens/Feeds.tsx:92 +msgid "Edit Saved Feeds" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:89 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:145 +#: src/view/com/modals/ChangeEmail.tsx:141 +#: src/view/com/modals/Waitlist.tsx:88 +msgid "Email" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:80 +msgid "Email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:111 +msgid "Email Updated" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:123 +msgid "Enable this setting to only see replies between people you follow." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:87 +msgid "Enter the address of your provider:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:372 +msgid "Enter the domain you want to use" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:85 +msgid "Enter your email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:117 +msgid "Enter your new email address below." +msgstr "" + +#: src/view/com/auth/login/Login.tsx:96 +msgid "Enter your username and password" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:151 +msgid "Expand alt text" +msgstr "" + +#: src/view/screens/Feeds.tsx:301 +msgid "Feed offline" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:163 +msgid "Feed Preferences" +msgstr "" + +#: src/view/shell/Drawer.tsx:365 +msgid "Feedback" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:154 +#: src/view/shell/Drawer.tsx:289 +msgid "Feeds" +msgstr "" + +#: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:119 +msgid "Finding similar accounts..." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:90 +msgid "Fine-tune the content you see on your home screen." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:43 +msgid "Fine-tune the discussion threads." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:459 +msgid "Follow" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:537 +msgid "following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:443 +msgid "Following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:492 +msgid "Follows you" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:99 +msgid "For security reasons, we'll need to send a confirmation code to your email address." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:192 +msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:229 +msgid "Forgot" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:226 +msgid "Forgot password" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:126 +#: src/view/com/auth/login/Login.tsx:143 +msgid "Forgot Password" +msgstr "" + +#: src/view/com/composer/photos/SelectPhotoBtn.tsx:43 +msgid "Gallery" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:178 +msgid "Get Started" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:105 +#: src/view/shell/desktop/LeftNav.tsx:92 +msgid "Go back" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:106 +#: src/view/screens/ProfileFeed.tsx:111 +#: src/view/screens/ProfileList.tsx:797 +#: src/view/screens/ProfileList.tsx:802 +msgid "Go Back" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:178 +#: src/view/com/auth/login/LoginForm.tsx:276 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:165 +msgid "Go to next" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:270 +msgid "Handle" +msgstr "" + +#: src/view/shell/Drawer.tsx:375 +msgid "Help" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:316 +msgid "Hide" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:308 +msgid "Hide user list" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:110 +#: src/view/shell/Drawer.tsx:240 +msgid "Home" +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:69 +#: src/view/screens/Settings.tsx:423 +msgid "Home Feed Preferences" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:111 +msgid "Hosting provider" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:92 +#: src/view/com/auth/create/Step1.tsx:97 +msgid "Hosting provider address" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:203 +msgid "I have a code" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:286 +msgid "I have my own domain" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:128 +msgid "If none are selected, suitable for all ages." +msgstr "" + +#: src/view/com/modals/AltImage.tsx:96 +msgid "Image alt text" +msgstr "" + +#: src/view/com/util/UserAvatar.tsx:300 +#: src/view/com/util/UserBanner.tsx:118 +msgid "Image options" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:109 +msgid "Invalid username or password" +msgstr "" + +#: src/view/screens/Settings.tsx:336 +msgid "Invite" +msgstr "" + +#: src/view/screens/Settings.tsx:329 +msgid "Invite a Friend" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:56 +msgid "Invite code" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:67 +msgid "Join the waitlist" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:67 +#: src/view/com/auth/create/Step2.tsx:71 +msgid "Join the waitlist." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:124 +msgid "Join Waitlist" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:97 +msgid "Language selection" +msgstr "" + +#: src/view/screens/Settings.tsx:471 +msgid "Languages" +msgstr "" + +#: src/view/com/util/moderation/PostAlerts.tsx:47 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:55 +#: src/view/com/util/moderation/ScreenHider.tsx:88 +msgid "Learn More" +msgstr "" + +#: src/view/com/util/moderation/ContentHider.tsx:75 +#: src/view/com/util/moderation/PostAlerts.tsx:40 +#: src/view/com/util/moderation/PostHider.tsx:76 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:47 +#: src/view/com/util/moderation/ScreenHider.tsx:85 +msgid "Learn more about this warning" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:76 +msgid "Leave them all unchecked to see any language." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:56 +msgid "Leaving Bluesky" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:127 +#: src/view/com/auth/login/Login.tsx:144 +msgid "Let's get your password reset!" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:479 +msgid "Like this feed" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:177 +msgid "List Avatar" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:190 +msgid "List Name" +msgstr "" + +#: src/view/shell/Drawer.tsx:297 +msgid "Lists" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:220 +#: src/view/com/post-thread/PostThread.tsx:228 +msgid "Load more posts" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:194 +msgid "Load new posts" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:50 +msgid "Local dev server" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:93 +msgid "Login to account that is not listed" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:70 +msgid "Make sure this is where you intend to go!" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:71 +msgid "Menu" +msgstr "" + +#: src/view/screens/Settings.tsx:487 +#: src/view/shell/Drawer.tsx:304 +msgid "Moderation" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:469 +#: src/view/screens/ProfileFeed.tsx:312 +#: src/view/screens/ProfileList.tsx:542 +msgid "More options" +msgstr "" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:158 +msgid "More post options" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:53 +msgid "My Birthday" +msgstr "" + +#: src/view/screens/Feeds.tsx:128 +msgid "My Feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:454 +msgid "My Saved Feeds" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:162 +#: src/view/com/modals/CreateOrEditList.tsx:202 +msgid "Name" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:76 +msgid "Never lose access to your followers and data." +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:203 +#: src/view/screens/Feeds.tsx:261 +#: src/view/screens/Profile.tsx:304 +#: src/view/screens/ProfileFeed.tsx:378 +#: src/view/screens/ProfileList.tsx:210 +#: src/view/screens/ProfileList.tsx:248 +#: src/view/shell/desktop/LeftNav.tsx:229 +msgid "New post" +msgstr "" + +#: src/view/shell/desktop/LeftNav.tsx:239 +msgid "New Post" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:105 +#: src/view/com/auth/login/LoginForm.tsx:279 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:158 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:168 +msgid "Next" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:137 +msgid "Next image" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:472 +#: src/view/screens/ProfileList.tsx:667 +msgid "No description" +msgstr "" + +#: src/view/screens/Feeds.tsx:207 +msgid "No results found for \"{query}\"" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:140 +#: src/view/shell/desktop/Search.tsx:112 +msgid "No results found for {0}" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:136 +msgid "Not Applicable" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:178 +#: src/view/shell/Drawer.tsx:262 +msgid "Notifications" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:41 +msgid "Okay" +msgstr "" + +#: src/view/com/composer/Composer.tsx:334 +msgid "One or more images is missing alt text." +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:50 +msgid "Open navigation" +msgstr "" + +#: src/view/screens/Settings.tsx:463 +msgid "Opens configurable language settings" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:284 +msgid "Opens modal for using custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:482 +msgid "Opens moderation settings" +msgstr "" + +#: src/view/screens/Settings.tsx:448 +msgid "Opens screen with all saved feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:501 +msgid "Opens the app password settings page" +msgstr "" + +#: src/view/screens/Settings.tsx:415 +msgid "Opens the home feed preferences" +msgstr "" + +#: src/view/screens/Settings.tsx:580 +msgid "Opens the storybook page" +msgstr "" + +#: src/view/screens/Settings.tsx:560 +msgid "Opens the system log page" +msgstr "" + +#: src/view/screens/Settings.tsx:432 +msgid "Opens the threads preferences" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:98 +msgid "Other account" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:88 +msgid "Other service" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:84 +msgid "Other..." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:100 +#: src/view/com/auth/create/Step2.tsx:110 +#: src/view/com/auth/login/LoginForm.tsx:214 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:132 +#: src/view/com/modals/DeleteAccount.tsx:183 +msgid "Password" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:28 +msgid "Password updated!" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:122 +msgid "Pictures meant for adults." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:67 +msgid "Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:125 +msgid "Please enter a unique name for this App Password or use our randomly generated one." +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:172 +msgid "Please enter your password as well:" +msgstr "" + +#: src/view/com/composer/Composer.tsx:317 +msgid "Post" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:343 +msgid "Post hidden" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:80 +msgid "Post language" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:69 +msgid "Post Languages" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:309 +msgid "Post not found" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:51 +msgid "Potentially Misleading Link" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:123 +msgid "Previous image" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:73 +msgid "Prioritize Your Follows" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:220 +#: src/view/shell/Drawer.tsx:161 +#: src/view/shell/Drawer.tsx:325 +msgid "Profile" +msgstr "" + +#: src/view/screens/Settings.tsx:689 +msgid "Protect your account by verifying your email." +msgstr "" + +#: src/view/com/modals/Repost.tsx:53 +msgid "Quote post" +msgstr "" + +#: src/view/com/modals/Repost.tsx:57 +msgid "Quote Post" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:236 +msgid "Ratios" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:84 +msgid "Remove" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:154 +msgid "Remove image" +msgstr "" + +#: src/view/com/composer/ExternalEmbed.tsx:70 +msgid "Remove image preview" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:120 +msgid "Reply Filters" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:165 +msgid "Report {collectionName}" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:37 +msgid "Report post" +msgstr "" + +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:69 +msgid "Repost or quote post" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:181 +#: src/view/com/modals/ChangeEmail.tsx:183 +msgid "Request Change" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:52 +msgid "Required for this provider" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:110 +msgid "Reset code" +msgstr "" + +#: src/view/screens/Settings.tsx:602 +msgid "Reset onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:592 +msgid "Reset preferences state" +msgstr "" + +#: src/view/screens/Settings.tsx:600 +msgid "Resets the onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:590 +msgid "Resets the preferences state" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:114 +#: src/view/com/auth/create/CreateAccount.tsx:118 +#: src/view/com/auth/login/LoginForm.tsx:256 +#: src/view/com/auth/login/LoginForm.tsx:259 +#: src/view/com/util/error/ErrorMessage.tsx:55 +#: src/view/com/util/error/ErrorScreen.tsx:65 +msgid "Retry" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:166 +msgid "Retry change handle" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:114 +#: src/view/com/modals/BirthDateSettings.tsx:90 +#: src/view/com/modals/BirthDateSettings.tsx:93 +#: src/view/com/modals/ChangeHandle.tsx:179 +#: src/view/com/modals/CreateOrEditList.tsx:240 +#: src/view/com/modals/CreateOrEditList.tsx:248 +#: src/view/com/modals/EditProfile.tsx:225 +msgid "Save" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:105 +msgid "Save alt text" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:210 +msgid "Save changes" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:233 +msgid "Save Changes" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:176 +msgid "Save handle change" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:144 +msgid "Save image crop" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:89 +#: src/view/com/search/HeaderWithInput.tsx:101 +#: src/view/com/util/forms/SearchInput.tsx:64 +#: src/view/shell/bottom-bar/BottomBar.tsx:132 +#: src/view/shell/desktop/Search.tsx:81 +#: src/view/shell/Drawer.tsx:218 +msgid "Search" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:110 +msgid "Security Step Required" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:30 +msgid "See what's next" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:75 +msgid "Select Bluesky Social" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:115 +msgid "Select from an existing account" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:141 +msgid "Select service" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:191 +msgid "Send Confirmation Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:119 +msgid "Send email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:130 +msgid "Send Email" +msgstr "" + +#: src/view/shell/Drawer.tsx:349 +#: src/view/shell/Drawer.tsx:370 +msgid "Send feedback" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:45 +msgid "Send Report" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:80 +msgid "Set new password" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:172 +msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:101 +msgid "Set this setting to \"No\" to hide all replies from your feed." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:155 +msgid "Set this setting to \"No\" to hide all reposts from your feed." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:96 +msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:191 +msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." +msgstr "" + +#: src/view/shell/Drawer.tsx:338 +msgid "Settings" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:126 +msgid "Sexual activity or erotic nudity." +msgstr "" + +#: src/view/screens/Settings.tsx:235 +msgid "Show" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:114 +msgid "Show anyway" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:188 +msgid "Show Posts from My Feeds" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:169 +msgid "Show Quote Posts" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:98 +msgid "Show Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:76 +msgid "Show replies by people you follow before all other replies." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:152 +msgid "Show Reposts" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:337 +msgid "Show users" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:95 +#: src/view/com/auth/SplashScreen.tsx:50 +msgid "Sign in" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:53 +#: src/view/com/auth/SplashScreen.web.tsx:59 +msgid "Sign In" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:65 +msgid "Sign in as {0}" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:56 +#: src/view/com/auth/login/Login.tsx:114 +msgid "Sign in as..." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:128 +msgid "Sign into" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:71 +#: src/view/com/modals/SwitchAccount.tsx:74 +#: src/view/screens/Settings.tsx:274 +#: src/view/screens/Settings.tsx:277 +msgid "Sign out" +msgstr "" + +#: src/view/screens/Settings.tsx:245 +msgid "Signed in as" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:37 +msgid "Skip" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:51 +msgid "Sort Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:54 +msgid "Sort replies to the same post by:" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:122 +msgid "Square" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:106 +#: src/view/com/modals/ServerInput.tsx:62 +msgid "Staging" +msgstr "" + +#: src/view/screens/Settings.tsx:624 +msgid "Status page" +msgstr "" + +#: src/view/screens/Settings.tsx:582 +msgid "Storybook" +msgstr "" + +#: src/view/screens/ProfileList.tsx:533 +msgid "Subscribe" +msgstr "" + +#: src/view/screens/ProfileList.tsx:529 +msgid "Subscribe to this list" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:47 +msgid "Switch Account" +msgstr "" + +#: src/view/screens/Settings.tsx:562 +msgid "System log" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:112 +msgid "Tall" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:50 +msgid "Text input field" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:312 +msgid "The post may have been deleted." +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:71 +msgid "This {screenDescription} has been flagged:" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:58 +msgid "This information is not shared with other users." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:107 +msgid "This is important in case you ever need to change your email or reset your password." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:71 +msgid "This is the service that keeps you online." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:63 +msgid "This link is taking you to the following website:" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:176 +msgid "This post has been deleted." +msgstr "" + +#: src/view/screens/Settings.tsx:441 +msgid "Thread Preferences" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:93 +msgid "Threaded Mode" +msgstr "" + +#: src/view/com/util/forms/DropdownButton.tsx:230 +msgid "Toggle dropdown" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:271 +msgid "Transformations" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:654 +msgid "Translate" +msgstr "" + +#: src/view/com/util/error/ErrorScreen.tsx:73 +msgid "Try again" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:74 +#: src/view/com/auth/login/LoginForm.tsx:113 +msgid "Unable to contact your service. Please check your Internet connection." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:386 +#: src/view/com/profile/ProfileHeader.tsx:389 +msgid "Unblock" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:185 +msgid "Update {displayName} in Lists" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:174 +msgid "Updating..." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:456 +msgid "Upload a text file to:" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:148 +msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:516 +msgid "Use default provider" +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:37 +msgid "User handle" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:168 +#: src/view/com/auth/login/LoginForm.tsx:185 +msgid "Username or email address" +msgstr "" + +#: src/view/screens/ProfileList.tsx:694 +msgid "Users" +msgstr "" + +#: src/view/screens/Settings.tsx:650 +msgid "Verify email" +msgstr "" + +#: src/view/screens/Settings.tsx:675 +msgid "Verify my email" +msgstr "" + +#: src/view/screens/Settings.tsx:684 +msgid "Verify My Email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:205 +#: src/view/com/modals/ChangeEmail.tsx:207 +msgid "Verify New Email" +msgstr "" + +#: src/view/screens/Log.tsx:53 +msgid "View debug entry" +msgstr "" + +#: src/view/com/profile/ProfileSubpageHeader.tsx:127 +msgid "View the avatar" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:80 +msgid "Visit Site" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:78 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:168 +msgid "What is the issue with this {collectionName}?" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:72 +msgid "Which languages are used in this post?" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:71 +msgid "Which languages would you like to see in your algorithmic feeds?" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:102 +msgid "Wide" +msgstr "" + +#: src/view/com/composer/Composer.tsx:390 +msgid "Write post" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:31 +msgid "Write your reply" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:122 +msgid "You can change hosting providers at any time." +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:31 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:346 +msgid "You have blocked the author or you have been blocked by the author." +msgstr "" + +#: src/view/com/lists/ListsList.tsx:112 +msgid "You have no lists." +msgstr "" + +#: src/view/screens/AppPasswords.tsx:55 +msgid "You have not created any app passwords yet. You can create one by pressing the button below." +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:83 +msgid "You will receive an email with a \"reset code.\" Enter that code here, then enter your new password." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:42 +msgid "Your account" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:121 +msgid "Your birth date" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:107 +msgid "Your email has been saved! We'll be in touch soon." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:125 +msgid "Your email has been updated but not verified. As a next step, please verify your new email." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:102 +msgid "Your email has not yet been verified. This is an important security step which we recommend." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:41 +#: src/view/com/modals/ChangeHandle.tsx:275 +msgid "Your full handle will be" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:69 +msgid "Your hosting provider" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:63 +msgid "Your posts, likes, and blocks are public. Mutes are private." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:27 +msgid "Your user handle" +msgstr "" diff --git a/src/locale/locales/hi/messages.js b/src/locale/locales/hi/messages.js new file mode 100644 index 000000000..8d85ab77c --- /dev/null +++ b/src/locale/locales/hi/messages.js @@ -0,0 +1 @@ +/*eslint-disable*/module.exports={messages:JSON.parse("{\"PBodTo\":\"- end of feed -\",\"EtUMsZ\":\". This warning is only available for posts with media attached.\",\"J/hVSQ\":[[\"0\"]],\"hZQerY\":[[\"0\"],\" \",[\"purposeLabel\"],\" List\"],\"6RmyWt\":\"<0>Here is your app password.</0> Use this to sign into the other app along with your handle.\",\"AnNF5e\":\"Accessibility\",\"AeXO77\":\"Account\",\"4WY4MD\":\"Account options\",\"m16xKo\":\"Add\",\"fBBX+K\":\"Add a content warning\",\"JU3hs2\":\"Add a user to this list\",\"MPPZ54\":\"Add account\",\"LkA8jz\":\"Add alt text\",\"Z8idyM\":\"Add details\",\"AoXl11\":\"Add details to report\",\"iE6B/9\":\"Add link card\",\"EXHdP1\":\"Add link card:\",\"x6laaL\":\"Add the following DNS record to your domain:\",\"jRrQFe\":\"Adjust the number of likes a reply must have to be shown in your feed.\",\"qLa52r\":\"Adult Content\",\"sxkWRg\":\"Advanced\",\"u2HO/d\":\"ALT\",\"u/DP73\":\"Alt text\",\"0QlT7/\":\"Alt text describes images for blind and low-vision users, and helps give context to everyone.\",\"woXbjq\":[\"An email has been sent to \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"Fon2dK\":[\"An email has been sent to your previous address, \",[\"0\"],\". It includes a confirmation code which you can enter below.\"],\"HZFm5R\":\"and\",\"SMmUnj\":\"App passwords\",\"aAIQg2\":\"Appearance\",\"EbvWd3\":\"Artistic or non-erotic nudity.\",\"iH8pgl\":\"Back\",\"ehOkF+\":\"Basics\",\"+gCI2a\":\"Birthday\",\"pieVBA\":\"Birthday:\",\"HFCE4A\":\"Blocked post.\",\"7A9u1j\":\"Bluesky\",\"ZHmKSm\":\"Bluesky is flexible.\",\"odLrdl\":\"Bluesky is open.\",\"/LsWK4\":\"Bluesky is public.\",\"C50OGr\":\"Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon.\",\"klVoaP\":\"Bluesky.Social\",\"qxBitM\":[\"Build version \",[\"0\"],\" \",[\"1\"]],\"rT2cV+\":\"Camera\",\"JGGrPC\":\"Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long.\",\"dEgA5A\":\"Cancel\",\"aMH9rr\":\"Cancel account deletion\",\"kc3E4R\":\"Cancel add image alt text\",\"wg4LHQ\":\"Cancel change handle\",\"hFL1Li\":\"Cancel image crop\",\"tijH8t\":\"Cancel profile editing\",\"Qe4C/d\":\"Cancel quote post\",\"5TviPn\":\"Cancel search\",\"nss3UV\":\"Cancel waitlist signup\",\"o+XJ9D\":\"Change\",\"pQco5R\":\"Change handle\",\"Q5e1U/\":\"Change Handle\",\"a3NAfL\":\"Change my email\",\"4vatyk\":\"Change Your Email\",\"/+X+/K\":\"Check your inbox for an email with the confirmation code to enter below:\",\"Rt502e\":\"Choose Service\",\"/L45sc\":\"Choose the algorithms that power your experience with custom feeds.\",\"Wk8hkn\":\"Choose your password\",\"QbvBNV\":\"Clear search query\",\"flH7u/\":\"Close alert\",\"hYmnbk\":\"Close bottom drawer\",\"47L1V1\":\"Close image\",\"l49ujN\":\"Close image viewer\",\"UryHFO\":\"Close navigation footer\",\"o8UUti\":\"Compose reply\",\"7VpPHA\":\"Confirm\",\"q8upsf\":\"Confirm Change\",\"8pNKIr\":\"Confirm content language settings\",\"tGg8Kt\":\"Confirm delete account\",\"ioZOzk\":\"Confirmation code\",\"J28zul\":\"Connecting...\",\"m8j6up\":\"Content Filtering\",\"/PlAsF\":\"Content Languages\",\"cogwXi\":\"Content Warning\",\"FG7AQv\":\"Content warnings\",\"6V3Ea3\":\"Copied\",\"he3ygx\":\"Copy\",\"7wWvgo\":\"Could not load feed\",\"8NNr/O\":\"Could not load list\",\"mpt9T+\":\"Create a new account\",\"IS0nrP\":\"Create Account\",\"6HbhpU\":\"Create new account\",\"GAD3Dx\":\"Custom domain\",\"ZQKLI1\":\"Danger Zone\",\"ZDGm40\":\"Delete account\",\"vzX5FB\":\"Delete Account\",\"gUEtxf\":\"Delete app password\",\"ktknoE\":\"Delete my account\",\"szz0+N\":\"Delete my account…\",\"u+1OHY\":\"Deleted post.\",\"Nu4oKW\":\"Description\",\"dacKHE\":\"Dev Server\",\"2ygkE8\":\"Developer Tools\",\"pbLwal\":\"Discover new feeds\",\"pfa8F0\":\"Display name\",\"0gS7M5\":\"Display Name\",\"iZ5pMB\":\"Domain verified!\",\"DPfwMq\":\"Done\",\"zT97vP\":[\"Done\",[\"extraText\"]],\"XQFMOm\":\"Edit image\",\"cLmurE\":\"Edit My Feeds\",\"bRZ5XW\":\"Edit my profile\",\"9OpVZg\":\"Edit profile\",\"QJQd1J\":\"Edit Profile\",\"Jn7kox\":\"Edit Saved Feeds\",\"O3oNi5\":\"Email\",\"ATGYL1\":\"Email address\",\"pJJ0Vp\":\"Email Updated\",\"96mted\":\"Enable this setting to only see replies between people you follow.\",\"YbIxza\":\"Enter the address of your provider:\",\"BfIgP6\":\"Enter the domain you want to use\",\"xRPn3U\":\"Enter your email address\",\"+inPGm\":\"Enter your new email address below.\",\"T0KLp4\":\"Enter your username and password\",\"0PkE20\":\"Expand alt text\",\"4yCy8i\":\"Feed offline\",\"N0CqyO\":\"Feed Preferences\",\"YirHq7\":\"Feedback\",\"2DoBvq\":\"Feeds\",\"Qzj1WT\":\"Finding similar accounts...\",\"QKSrQV\":\"Fine-tune the content you see on your home screen.\",\"r+KeyR\":\"Fine-tune the discussion threads.\",\"MKEPCY\":\"Follow\",\"NIjL2Y\":\"following\",\"y6sq5j\":\"Following\",\"p3UO/y\":\"Follows you\",\"5RhDkD\":\"For security reasons, we'll need to send a confirmation code to your email address.\",\"NJPhAO\":\"For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one.\",\"5bDfuq\":\"Forgot\",\"hEPLrs\":\"Forgot password\",\"dn8X5t\":\"Forgot Password\",\"U+kFad\":\"Gallery\",\"c3b0B0\":\"Get Started\",\"CKyk7Q\":\"Go back\",\"sr0UJD\":\"Go Back\",\"Rtp0y7\":\"Go to next\",\"Nf7oXL\":\"Handle\",\"c3XJ18\":\"Help\",\"vLyv1R\":\"Hide\",\"qdOx2q\":\"Hide user list\",\"i0qMbr\":\"Home\",\"sXZ8IU\":\"Home Feed Preferences\",\"yt7fhu\":\"Hosting provider\",\"s2xA6t\":\"Hosting provider address\",\"o+axy6\":\"I have a code\",\"wey2os\":\"I have my own domain\",\"WlEcKr\":\"If none are selected, suitable for all ages.\",\"VCk0rR\":\"Image alt text\",\"STGpNQ\":\"Image options\",\"dSKHAa\":\"Invalid username or password\",\"MFKlMB\":\"Invite\",\"F5MZVk\":\"Invite a Friend\",\"6KlkHI\":\"Invite code\",\"F75w8j\":\"Join the waitlist\",\"6iVTdm\":\"Join the waitlist.\",\"SNzppu\":\"Join Waitlist\",\"Dcq5kL\":\"Language selection\",\"GAmD3h\":\"Languages\",\"NgeSlx\":\"Learn More\",\"rj0Lke\":\"Learn more about this warning\",\"kq2ga7\":\"Leave them all unchecked to see any language.\",\"QfDITI\":\"Leaving Bluesky\",\"Esfg1M\":\"Let's get your password reset!\",\"BvSY1i\":\"Like this feed\",\"FuZWua\":\"List Avatar\",\"8mjA4F\":\"List Name\",\"h16FyT\":\"Lists\",\"ujW4FW\":\"Load more posts\",\"VkLESX\":\"Load new posts\",\"jl0AFf\":\"Local dev server\",\"cR9UpQ\":\"Login to account that is not listed\",\"2U/gDT\":\"Make sure this is where you intend to go!\",\"zucql+\":\"Menu\",\"DzmsLV\":\"Moderation\",\"3Siwmw\":\"More options\",\"Y17r45\":\"More post options\",\"Mysqyf\":\"My Birthday\",\"6MBNS/\":\"My Feeds\",\"Ha6iBv\":\"My Saved Feeds\",\"6YtxFj\":\"Name\",\"8yolS6\":\"Never lose access to your followers and data.\",\"2B7HLH\":\"New post\",\"FGrimz\":\"New Post\",\"hXzOVo\":\"Next\",\"EatZYJ\":\"Next image\",\"flmDTf\":\"No description\",\"fOlAiK\":[\"No results found for \\\"\",[\"query\"],\"\\\"\"],\"kA9DpB\":[\"No results found for \",[\"0\"]],\"ispbnl\":\"Not Applicable\",\"iDNBZe\":\"Notifications\",\"UaXeX3\":\"Okay\",\"Cqo2D+\":\"One or more images is missing alt text.\",\"M/Q2aG\":\"Open navigation\",\"M5PuNq\":\"Opens configurable language settings\",\"eSqpax\":\"Opens modal for using custom domain\",\"vYwHHI\":\"Opens moderation settings\",\"0tHyB7\":\"Opens screen with all saved feeds\",\"nmRoY/\":\"Opens the app password settings page\",\"6e9Apv\":\"Opens the home feed preferences\",\"O87Dr/\":\"Opens the storybook page\",\"G+PVmg\":\"Opens the system log page\",\"Jqb7sy\":\"Opens the threads preferences\",\"b22AVl\":\"Other account\",\"n+HLOP\":\"Other service\",\"1PKxQ7\":\"Other...\",\"8ZsakT\":\"Password\",\"DKeVgZ\":\"Password updated!\",\"VeZE5Q\":\"Pictures meant for adults.\",\"Apyknf\":\"Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed.\",\"9qpQ5O\":\"Please enter a unique name for this App Password or use our randomly generated one.\",\"QJr5Xp\":\"Please enter your password as well:\",\"y28hnO\":\"Post\",\"h5RcXU\":\"Post hidden\",\"r5zLS0\":\"Post language\",\"AzCucI\":\"Post Languages\",\"tJFPmV\":\"Post not found\",\"0+DQbr\":\"Potentially Misleading Link\",\"MHk+7g\":\"Previous image\",\"x8iR7V\":\"Prioritize Your Follows\",\"vERlcd\":\"Profile\",\"MrgqOW\":\"Protect your account by verifying your email.\",\"8HFFRQ\":\"Quote post\",\"+KrAHa\":\"Quote Post\",\"WlWsdE\":\"Ratios\",\"t/YqKh\":\"Remove\",\"5ywtDz\":\"Remove image\",\"Dw/XUh\":\"Remove image preview\",\"Veu9gK\":\"Reply Filters\",\"lQWbAr\":[\"Report \",[\"collectionName\"]],\"6IcSvC\":\"Report post\",\"JOV5dR\":\"Repost or quote post\",\"bqG37Z\":\"Request Change\",\"8XIT+P\":\"Required for this provider\",\"vJgYMA\":\"Reset code\",\"xEL92I\":\"Reset onboarding state\",\"bee/Fw\":\"Reset preferences state\",\"wToeoz\":\"Resets the onboarding state\",\"nIU7qI\":\"Resets the preferences state\",\"6gRgw8\":\"Retry\",\"hAbYQa\":\"Retry change handle\",\"tfDRzk\":\"Save\",\"KV2YQQ\":\"Save alt text\",\"y3aU20\":\"Save changes\",\"IUwGEM\":\"Save Changes\",\"Xs07Tg\":\"Save handle change\",\"BckA7m\":\"Save image crop\",\"A1taO8\":\"Search\",\"CKROFy\":\"Security Step Required\",\"cNzyJW\":\"See what's next\",\"L5sM7N\":\"Select Bluesky Social\",\"o3dwub\":\"Select from an existing account\",\"GGw2AK\":\"Select service\",\"vp9yIB\":\"Send Confirmation Email\",\"65dxv8\":\"Send email\",\"i/TzEU\":\"Send Email\",\"RoafuO\":\"Send feedback\",\"4cijjm\":\"Send Report\",\"V/e7nf\":\"Set new password\",\"gwsie4\":\"Set this setting to \\\"No\\\" to hide all quote posts from your feed. Reposts will still be visible.\",\"IZjC3J\":\"Set this setting to \\\"No\\\" to hide all replies from your feed.\",\"KIgU3l\":\"Set this setting to \\\"No\\\" to hide all reposts from your feed.\",\"zaAyrz\":\"Set this setting to \\\"Yes\\\" to show replies in a threaded view. This is an experimental feature.\",\"fQV2eE\":\"Set this setting to \\\"Yes\\\" to show samples of your saved feeds in your following feed. This is an experimental feature.\",\"Tz0i8g\":\"Settings\",\"HfWHhJ\":\"Sexual activity or erotic nudity.\",\"8vETh9\":\"Show\",\"aWAdCb\":\"Show anyway\",\"NijgXr\":\"Show Posts from My Feeds\",\"T3Mt8m\":\"Show Quote Posts\",\"BlW8X/\":\"Show Replies\",\"X4GwDb\":\"Show replies by people you follow before all other replies.\",\"GiogzH\":\"Show Reposts\",\"fhY/fL\":\"Show users\",\"5lWFkC\":\"Sign in\",\"n1ekoW\":\"Sign In\",\"N9o7n5\":[\"Sign in as \",[\"0\"]],\"FT1MVS\":\"Sign in as...\",\"+UpfFC\":\"Sign into\",\"fcWrnU\":\"Sign out\",\"zU+Ro7\":\"Signed in as\",\"6Uau97\":\"Skip\",\"0o5BFH\":\"Sort Replies\",\"GH1Rgk\":\"Sort replies to the same post by:\",\"1DA6ap\":\"Square\",\"aKEHLj\":\"Staging\",\"tgEXwM\":\"Status page\",\"P5jja7\":\"Storybook\",\"EDl9kS\":\"Subscribe\",\"5z3ICN\":\"Subscribe to this list\",\"VjWeLI\":\"Switch Account\",\"fP8jTZ\":\"System log\",\"HF6Iah\":\"Tall\",\"p8Iz39\":\"Text input field\",\"G4EksE\":\"The post may have been deleted.\",\"KRYn8w\":[\"This \",[\"screenDescription\"],\" has been flagged:\"],\"lm845B\":\"This information is not shared with other users.\",\"5Pvw/O\":\"This is important in case you ever need to change your email or reset your password.\",\"sQQfZ9\":\"This is the service that keeps you online.\",\"CvX8qs\":\"This link is taking you to the following website:\",\"WKrUVy\":\"This post has been deleted.\",\"u9ThjD\":\"Thread Preferences\",\"zmXsk5\":\"Threaded Mode\",\"1x30Qt\":\"Toggle dropdown\",\"KFXQEt\":\"Transformations\",\"pi8x/S\":\"Translate\",\"KDw4GX\":\"Try again\",\"nc4Wfd\":\"Unable to contact your service. Please check your Internet connection.\",\"tuS5Jz\":\"Unblock\",\"vaz2uI\":[\"Update \",[\"displayName\"],\" in Lists\"],\"RXbEvi\":\"Updating...\",\"Vwkfp4\":\"Upload a text file to:\",\"jTdnU6\":\"Use app passwords to login to other Bluesky clients without giving full access to your account or password.\",\"CH1am9\":\"Use default provider\",\"t4Yp4Z\":\"User handle\",\"nZx9mr\":\"Username or email address\",\"Sxm8rQ\":\"Users\",\"MBOY4U\":\"Verify email\",\"Ejyv0o\":\"Verify my email\",\"9czCrB\":\"Verify My Email\",\"ibSVGR\":\"Verify New Email\",\"nHsQde\":\"View debug entry\",\"47jzzd\":\"View the avatar\",\"wK4H1r\":\"Visit Site\",\"qjBGxf\":\"We're so excited to have you join us!\",\"Mj7rl/\":[\"What is the issue with this \",[\"collectionName\"],\"?\"],\"3qn29J\":\"Which languages are used in this post?\",\"uawiGa\":\"Which languages would you like to see in your algorithmic feeds?\",\"I5S9ZE\":\"Wide\",\"y02THm\":\"Write post\",\"6ckZRB\":\"Write your reply\",\"STPj0e\":\"You can change hosting providers at any time.\",\"67nRLM\":\"You can now sign in with your new password.\",\"RkXibf\":\"You have blocked the author or you have been blocked by the author.\",\"tCLJ9E\":\"You have no lists.\",\"NDgp3i\":\"You have not created any app passwords yet. You can create one by pressing the button below.\",\"RrDyEb\":\"You will receive an email with a \\\"reset code.\\\" Enter that code here, then enter your new password.\",\"gdRnT7\":\"Your account\",\"k7hmsH\":\"Your birth date\",\"OubkcP\":\"Your email has been saved! We'll be in touch soon.\",\"z2L+/9\":\"Your email has been updated but not verified. As a next step, please verify your new email.\",\"XZlIVw\":\"Your email has not yet been verified. This is an important security step which we recommend.\",\"qv9f4I\":\"Your full handle will be\",\"lvcqqG\":\"Your hosting provider\",\"Oqt/PG\":\"Your posts, likes, and blocks are public. Mutes are private.\",\"MvWO9d\":\"Your user handle\"}")}; \ No newline at end of file diff --git a/src/locale/locales/hi/messages.po b/src/locale/locales/hi/messages.po new file mode 100644 index 000000000..f3241668e --- /dev/null +++ b/src/locale/locales/hi/messages.po @@ -0,0 +1,1544 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-11-06 12:28-0800\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: hi\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/view/screens/Profile.tsx:212 +msgid "- end of feed -" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:138 +msgid ". This warning is only available for posts with media attached." +msgstr "" + +#: src/view/com/modals/Repost.tsx:45 +msgid "{0}" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:167 +msgid "{0} {purposeLabel} List" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:132 +msgid "<0>Here is your app password.</0> Use this to sign into the other app along with your handle." +msgstr "" + +#: src/view/com/modals/EditImage.tsx:299 +#: src/view/screens/Settings.tsx:363 +msgid "Accessibility" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:157 +#: src/view/screens/Settings.tsx:201 +msgid "Account" +msgstr "" + +#: src/view/com/util/AccountDropdownBtn.tsx:41 +msgid "Account options" +msgstr "" + +#: src/view/screens/ProfileList.tsx:710 +msgid "Add" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:57 +msgid "Add a content warning" +msgstr "" + +#: src/view/screens/ProfileList.tsx:700 +msgid "Add a user to this list" +msgstr "" + +#: src/view/screens/Settings.tsx:313 +#: src/view/screens/Settings.tsx:322 +msgid "Add account" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:119 +#: src/view/com/composer/photos/Gallery.tsx:167 +msgid "Add alt text" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:41 +#: src/view/com/modals/report/Modal.tsx:190 +msgid "Add details" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:193 +msgid "Add details to report" +msgstr "" + +#: src/view/com/composer/Composer.tsx:419 +msgid "Add link card" +msgstr "" + +#: src/view/com/composer/Composer.tsx:422 +msgid "Add link card:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:418 +msgid "Add the following DNS record to your domain:" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:140 +msgid "Adjust the number of likes a reply must have to be shown in your feed." +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:76 +msgid "Adult Content" +msgstr "" + +#: src/view/screens/Settings.tsx:493 +msgid "Advanced" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:127 +msgid "ALT" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:315 +msgid "Alt text" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:193 +msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:112 +msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:119 +msgid "An email has been sent to your previous address, {0}. It includes a confirmation code which you can enter below." +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:238 +msgid "and" +msgstr "" + +#: src/view/screens/Settings.tsx:509 +msgid "App passwords" +msgstr "" + +#: src/view/screens/Settings.tsx:378 +msgid "Appearance" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:124 +msgid "Artistic or non-erotic nudity." +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:92 +#: src/view/com/auth/login/ChooseAccountForm.tsx:111 +#: src/view/com/auth/login/LoginForm.tsx:247 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:150 +#: src/view/com/modals/report/InputIssueDetails.tsx:45 +#: src/view/com/post-thread/PostThread.tsx:317 +#: src/view/com/post-thread/PostThread.tsx:325 +#: src/view/com/post-thread/PostThread.tsx:354 +#: src/view/com/post-thread/PostThread.tsx:362 +#: src/view/com/profile/ProfileHeader.tsx:576 +msgid "Back" +msgstr "" + +#: src/view/screens/Settings.tsx:407 +msgid "Basics" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:130 +#: src/view/com/modals/BirthDateSettings.tsx:69 +msgid "Birthday" +msgstr "" + +#: src/view/screens/Settings.tsx:228 +msgid "Birthday:" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:211 +msgid "Blocked post." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:52 +#: src/view/com/auth/SplashScreen.tsx:27 +msgid "Bluesky" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:84 +msgid "Bluesky is flexible." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:73 +msgid "Bluesky is open." +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:60 +msgid "Bluesky is public." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:70 +msgid "Bluesky uses invites to build a healthier community. If you don't know anybody with an invite, you can sign up for the waitlist and we'll send one soon." +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:78 +msgid "Bluesky.Social" +msgstr "" + +#: src/view/screens/Settings.tsx:612 +msgid "Build version {0} {1}" +msgstr "" + +#: src/view/com/composer/photos/OpenCameraBtn.tsx:62 +msgid "Camera" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:199 +msgid "Can only contain letters, numbers, spaces, dashes, and underscores. Must be at least 4 characters long, but no more than 32 characters long." +msgstr "" + +#: src/view/com/composer/Composer.tsx:271 +#: src/view/com/composer/Composer.tsx:274 +#: src/view/com/modals/AltImage.tsx:127 +#: src/view/com/modals/ChangeEmail.tsx:216 +#: src/view/com/modals/ChangeEmail.tsx:218 +#: src/view/com/modals/Confirm.tsx:89 +#: src/view/com/modals/CreateOrEditList.tsx:258 +#: src/view/com/modals/CreateOrEditList.tsx:263 +#: src/view/com/modals/DeleteAccount.tsx:142 +#: src/view/com/modals/DeleteAccount.tsx:215 +#: src/view/com/modals/EditImage.tsx:323 +#: src/view/com/modals/EditProfile.tsx:250 +#: src/view/com/modals/LinkWarning.tsx:90 +#: src/view/com/modals/Repost.tsx:74 +#: src/view/com/modals/UserAddRemoveLists.tsx:199 +#: src/view/com/modals/Waitlist.tsx:136 +#: src/view/com/search/HeaderWithInput.tsx:127 +#: src/view/shell/desktop/Search.tsx:93 +msgid "Cancel" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:138 +#: src/view/com/modals/DeleteAccount.tsx:211 +msgid "Cancel account deletion" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:122 +msgid "Cancel add image alt text" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:144 +msgid "Cancel change handle" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:134 +msgid "Cancel image crop" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:245 +msgid "Cancel profile editing" +msgstr "" + +#: src/view/com/modals/Repost.tsx:65 +msgid "Cancel quote post" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:101 +#: src/view/shell/desktop/Search.tsx:89 +msgid "Cancel search" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:132 +msgid "Cancel waitlist signup" +msgstr "" + +#: src/view/screens/Settings.tsx:222 +msgid "Change" +msgstr "" + +#: src/view/screens/Settings.tsx:517 +#: src/view/screens/Settings.tsx:526 +msgid "Change handle" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:156 +msgid "Change Handle" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:136 +msgid "Change my email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:109 +msgid "Change Your Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:155 +msgid "Check your inbox for an email with the confirmation code to enter below:" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:38 +msgid "Choose Service" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:87 +msgid "Choose the algorithms that power your experience with custom feeds." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:105 +msgid "Choose your password" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:111 +#: src/view/com/util/forms/SearchInput.tsx:73 +msgid "Clear search query" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:38 +msgid "Close alert" +msgstr "" + +#: src/view/com/util/BottomSheetCustomBackdrop.tsx:33 +msgid "Close bottom drawer" +msgstr "" + +#: src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx:26 +msgid "Close image" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:107 +msgid "Close image viewer" +msgstr "" + +#: src/view/shell/index.web.tsx:65 +msgid "Close navigation footer" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:22 +msgid "Compose reply" +msgstr "" + +#: src/view/com/modals/Confirm.tsx:76 +#: src/view/com/modals/SelfLabel.tsx:156 +#: src/view/com/modals/VerifyEmail.tsx:220 +#: src/view/screens/PreferencesHomeFeed.tsx:223 +#: src/view/screens/PreferencesThreads.tsx:128 +msgid "Confirm" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:193 +#: src/view/com/modals/ChangeEmail.tsx:195 +msgid "Confirm Change" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:34 +msgid "Confirm content language settings" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:201 +msgid "Confirm delete account" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:157 +#: src/view/com/modals/DeleteAccount.tsx:168 +#: src/view/com/modals/VerifyEmail.tsx:154 +msgid "Confirmation code" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:125 +#: src/view/com/auth/login/LoginForm.tsx:266 +msgid "Connecting..." +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:42 +msgid "Content Filtering" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:68 +msgid "Content Languages" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:68 +msgid "Content Warning" +msgstr "" + +#: src/view/com/composer/labels/LabelsBtn.tsx:32 +msgid "Content warnings" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:178 +#: src/view/com/modals/InviteCodes.tsx:129 +msgid "Copied" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:171 +msgid "Copy" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:97 +msgid "Could not load feed" +msgstr "" + +#: src/view/screens/ProfileList.tsx:788 +msgid "Could not load list" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:42 +msgid "Create a new account" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:77 +msgid "Create Account" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:39 +msgid "Create new account" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:390 +#: src/view/com/modals/ServerInput.tsx:102 +msgid "Custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:531 +msgid "Danger Zone" +msgstr "" + +#: src/view/screens/Settings.tsx:538 +msgid "Delete account" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:75 +msgid "Delete Account" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:193 +msgid "Delete app password" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:204 +msgid "Delete my account" +msgstr "" + +#: src/view/screens/Settings.tsx:548 +msgid "Delete my account…" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:203 +msgid "Deleted post." +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:209 +#: src/view/com/modals/CreateOrEditList.tsx:225 +#: src/view/com/modals/EditProfile.tsx:199 +#: src/view/com/modals/EditProfile.tsx:211 +msgid "Description" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:112 +msgid "Dev Server" +msgstr "" + +#: src/view/screens/Settings.tsx:553 +msgid "Developer Tools" +msgstr "" + +#: src/view/screens/Feeds.tsx:165 +msgid "Discover new feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:193 +msgid "Display name" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:181 +msgid "Display Name" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:488 +msgid "Domain verified!" +msgstr "" + +#: src/view/com/modals/ContentFilteringSettings.tsx:77 +#: src/view/com/modals/ContentFilteringSettings.tsx:85 +#: src/view/com/modals/crop-image/CropImage.web.tsx:152 +#: src/view/com/modals/EditImage.tsx:333 +#: src/view/com/modals/ListAddUser.tsx:153 +#: src/view/com/modals/SelfLabel.tsx:159 +#: src/view/screens/PreferencesHomeFeed.tsx:226 +#: src/view/screens/PreferencesThreads.tsx:131 +msgid "Done" +msgstr "" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:42 +msgid "Done{extraText}" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:141 +#: src/view/com/modals/EditImage.tsx:207 +msgid "Edit image" +msgstr "" + +#: src/view/screens/Feeds.tsx:132 +msgid "Edit My Feeds" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:158 +msgid "Edit my profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:373 +msgid "Edit profile" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:376 +msgid "Edit Profile" +msgstr "" + +#: src/view/screens/Feeds.tsx:92 +msgid "Edit Saved Feeds" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:89 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:145 +#: src/view/com/modals/ChangeEmail.tsx:141 +#: src/view/com/modals/Waitlist.tsx:88 +msgid "Email" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:80 +msgid "Email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:111 +msgid "Email Updated" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:123 +msgid "Enable this setting to only see replies between people you follow." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:87 +msgid "Enter the address of your provider:" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:372 +msgid "Enter the domain you want to use" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:85 +msgid "Enter your email address" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:117 +msgid "Enter your new email address below." +msgstr "" + +#: src/view/com/auth/login/Login.tsx:96 +msgid "Enter your username and password" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:151 +msgid "Expand alt text" +msgstr "" + +#: src/view/screens/Feeds.tsx:301 +msgid "Feed offline" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:163 +msgid "Feed Preferences" +msgstr "" + +#: src/view/shell/Drawer.tsx:365 +msgid "Feedback" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:154 +#: src/view/shell/Drawer.tsx:289 +msgid "Feeds" +msgstr "" + +#: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:119 +msgid "Finding similar accounts..." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:90 +msgid "Fine-tune the content you see on your home screen." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:43 +msgid "Fine-tune the discussion threads." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:459 +msgid "Follow" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:537 +msgid "following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:443 +msgid "Following" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:492 +msgid "Follows you" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:99 +msgid "For security reasons, we'll need to send a confirmation code to your email address." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:192 +msgid "For security reasons, you won't be able to view this again. If you lose this password, you'll need to generate a new one." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:229 +msgid "Forgot" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:226 +msgid "Forgot password" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:126 +#: src/view/com/auth/login/Login.tsx:143 +msgid "Forgot Password" +msgstr "" + +#: src/view/com/composer/photos/SelectPhotoBtn.tsx:43 +msgid "Gallery" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:178 +msgid "Get Started" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:105 +#: src/view/shell/desktop/LeftNav.tsx:92 +msgid "Go back" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:106 +#: src/view/screens/ProfileFeed.tsx:111 +#: src/view/screens/ProfileList.tsx:797 +#: src/view/screens/ProfileList.tsx:802 +msgid "Go Back" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:178 +#: src/view/com/auth/login/LoginForm.tsx:276 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:165 +msgid "Go to next" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:270 +msgid "Handle" +msgstr "" + +#: src/view/shell/Drawer.tsx:375 +msgid "Help" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:316 +msgid "Hide" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:308 +msgid "Hide user list" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:110 +#: src/view/shell/Drawer.tsx:240 +msgid "Home" +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:69 +#: src/view/screens/Settings.tsx:423 +msgid "Home Feed Preferences" +msgstr "" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:111 +msgid "Hosting provider" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:92 +#: src/view/com/auth/create/Step1.tsx:97 +msgid "Hosting provider address" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:203 +msgid "I have a code" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:286 +msgid "I have my own domain" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:128 +msgid "If none are selected, suitable for all ages." +msgstr "" + +#: src/view/com/modals/AltImage.tsx:96 +msgid "Image alt text" +msgstr "" + +#: src/view/com/util/UserAvatar.tsx:300 +#: src/view/com/util/UserBanner.tsx:118 +msgid "Image options" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:109 +msgid "Invalid username or password" +msgstr "" + +#: src/view/screens/Settings.tsx:336 +msgid "Invite" +msgstr "" + +#: src/view/screens/Settings.tsx:329 +msgid "Invite a Friend" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:56 +msgid "Invite code" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:67 +msgid "Join the waitlist" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:67 +#: src/view/com/auth/create/Step2.tsx:71 +msgid "Join the waitlist." +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:124 +msgid "Join Waitlist" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:97 +msgid "Language selection" +msgstr "" + +#: src/view/screens/Settings.tsx:471 +msgid "Languages" +msgstr "" + +#: src/view/com/util/moderation/PostAlerts.tsx:47 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:55 +#: src/view/com/util/moderation/ScreenHider.tsx:88 +msgid "Learn More" +msgstr "" + +#: src/view/com/util/moderation/ContentHider.tsx:75 +#: src/view/com/util/moderation/PostAlerts.tsx:40 +#: src/view/com/util/moderation/PostHider.tsx:76 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:47 +#: src/view/com/util/moderation/ScreenHider.tsx:85 +msgid "Learn more about this warning" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:76 +msgid "Leave them all unchecked to see any language." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:56 +msgid "Leaving Bluesky" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:127 +#: src/view/com/auth/login/Login.tsx:144 +msgid "Let's get your password reset!" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:479 +msgid "Like this feed" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:177 +msgid "List Avatar" +msgstr "" + +#: src/view/com/modals/CreateOrEditList.tsx:190 +msgid "List Name" +msgstr "" + +#: src/view/shell/Drawer.tsx:297 +msgid "Lists" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:220 +#: src/view/com/post-thread/PostThread.tsx:228 +msgid "Load more posts" +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:194 +msgid "Load new posts" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:50 +msgid "Local dev server" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:93 +msgid "Login to account that is not listed" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:70 +msgid "Make sure this is where you intend to go!" +msgstr "" + +#: src/view/com/search/HeaderWithInput.tsx:71 +msgid "Menu" +msgstr "" + +#: src/view/screens/Settings.tsx:487 +#: src/view/shell/Drawer.tsx:304 +msgid "Moderation" +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:469 +#: src/view/screens/ProfileFeed.tsx:312 +#: src/view/screens/ProfileList.tsx:542 +msgid "More options" +msgstr "" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:158 +msgid "More post options" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:53 +msgid "My Birthday" +msgstr "" + +#: src/view/screens/Feeds.tsx:128 +msgid "My Feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:454 +msgid "My Saved Feeds" +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:162 +#: src/view/com/modals/CreateOrEditList.tsx:202 +msgid "Name" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:76 +msgid "Never lose access to your followers and data." +msgstr "" + +#: src/view/com/feeds/FeedPage.tsx:203 +#: src/view/screens/Feeds.tsx:261 +#: src/view/screens/Profile.tsx:304 +#: src/view/screens/ProfileFeed.tsx:378 +#: src/view/screens/ProfileList.tsx:210 +#: src/view/screens/ProfileList.tsx:248 +#: src/view/shell/desktop/LeftNav.tsx:229 +msgid "New post" +msgstr "" + +#: src/view/shell/desktop/LeftNav.tsx:239 +msgid "New Post" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:105 +#: src/view/com/auth/login/LoginForm.tsx:279 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:158 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:168 +msgid "Next" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:137 +msgid "Next image" +msgstr "" + +#: src/view/screens/ProfileFeed.tsx:472 +#: src/view/screens/ProfileList.tsx:667 +msgid "No description" +msgstr "" + +#: src/view/screens/Feeds.tsx:207 +msgid "No results found for \"{query}\"" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:140 +#: src/view/shell/desktop/Search.tsx:112 +msgid "No results found for {0}" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:136 +msgid "Not Applicable" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:178 +#: src/view/shell/Drawer.tsx:262 +msgid "Notifications" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:41 +msgid "Okay" +msgstr "" + +#: src/view/com/composer/Composer.tsx:334 +msgid "One or more images is missing alt text." +msgstr "" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:50 +msgid "Open navigation" +msgstr "" + +#: src/view/screens/Settings.tsx:463 +msgid "Opens configurable language settings" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:284 +msgid "Opens modal for using custom domain" +msgstr "" + +#: src/view/screens/Settings.tsx:482 +msgid "Opens moderation settings" +msgstr "" + +#: src/view/screens/Settings.tsx:448 +msgid "Opens screen with all saved feeds" +msgstr "" + +#: src/view/screens/Settings.tsx:501 +msgid "Opens the app password settings page" +msgstr "" + +#: src/view/screens/Settings.tsx:415 +msgid "Opens the home feed preferences" +msgstr "" + +#: src/view/screens/Settings.tsx:580 +msgid "Opens the storybook page" +msgstr "" + +#: src/view/screens/Settings.tsx:560 +msgid "Opens the system log page" +msgstr "" + +#: src/view/screens/Settings.tsx:432 +msgid "Opens the threads preferences" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:98 +msgid "Other account" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:88 +msgid "Other service" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:84 +msgid "Other..." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:100 +#: src/view/com/auth/create/Step2.tsx:110 +#: src/view/com/auth/login/LoginForm.tsx:214 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:132 +#: src/view/com/modals/DeleteAccount.tsx:183 +msgid "Password" +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:28 +msgid "Password updated!" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:122 +msgid "Pictures meant for adults." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:67 +msgid "Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed." +msgstr "" + +#: src/view/com/modals/AddAppPasswords.tsx:125 +msgid "Please enter a unique name for this App Password or use our randomly generated one." +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:172 +msgid "Please enter your password as well:" +msgstr "" + +#: src/view/com/composer/Composer.tsx:317 +msgid "Post" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:343 +msgid "Post hidden" +msgstr "" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:80 +msgid "Post language" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:69 +msgid "Post Languages" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:309 +msgid "Post not found" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:51 +msgid "Potentially Misleading Link" +msgstr "" + +#: src/view/com/lightbox/Lightbox.web.tsx:123 +msgid "Previous image" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:73 +msgid "Prioritize Your Follows" +msgstr "" + +#: src/view/shell/bottom-bar/BottomBar.tsx:220 +#: src/view/shell/Drawer.tsx:161 +#: src/view/shell/Drawer.tsx:325 +msgid "Profile" +msgstr "" + +#: src/view/screens/Settings.tsx:689 +msgid "Protect your account by verifying your email." +msgstr "" + +#: src/view/com/modals/Repost.tsx:53 +msgid "Quote post" +msgstr "" + +#: src/view/com/modals/Repost.tsx:57 +msgid "Quote Post" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:236 +msgid "Ratios" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:84 +msgid "Remove" +msgstr "" + +#: src/view/com/composer/photos/Gallery.tsx:154 +msgid "Remove image" +msgstr "" + +#: src/view/com/composer/ExternalEmbed.tsx:70 +msgid "Remove image preview" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:120 +msgid "Reply Filters" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:165 +msgid "Report {collectionName}" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:37 +msgid "Report post" +msgstr "" + +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:69 +msgid "Repost or quote post" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:181 +#: src/view/com/modals/ChangeEmail.tsx:183 +msgid "Request Change" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:52 +msgid "Required for this provider" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:110 +msgid "Reset code" +msgstr "" + +#: src/view/screens/Settings.tsx:602 +msgid "Reset onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:592 +msgid "Reset preferences state" +msgstr "" + +#: src/view/screens/Settings.tsx:600 +msgid "Resets the onboarding state" +msgstr "" + +#: src/view/screens/Settings.tsx:590 +msgid "Resets the preferences state" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:114 +#: src/view/com/auth/create/CreateAccount.tsx:118 +#: src/view/com/auth/login/LoginForm.tsx:256 +#: src/view/com/auth/login/LoginForm.tsx:259 +#: src/view/com/util/error/ErrorMessage.tsx:55 +#: src/view/com/util/error/ErrorScreen.tsx:65 +msgid "Retry" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:166 +msgid "Retry change handle" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:114 +#: src/view/com/modals/BirthDateSettings.tsx:90 +#: src/view/com/modals/BirthDateSettings.tsx:93 +#: src/view/com/modals/ChangeHandle.tsx:179 +#: src/view/com/modals/CreateOrEditList.tsx:240 +#: src/view/com/modals/CreateOrEditList.tsx:248 +#: src/view/com/modals/EditProfile.tsx:225 +msgid "Save" +msgstr "" + +#: src/view/com/modals/AltImage.tsx:105 +msgid "Save alt text" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:210 +msgid "Save changes" +msgstr "" + +#: src/view/com/modals/EditProfile.tsx:233 +msgid "Save Changes" +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:176 +msgid "Save handle change" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:144 +msgid "Save image crop" +msgstr "" + +#: src/view/com/modals/ListAddUser.tsx:89 +#: src/view/com/search/HeaderWithInput.tsx:101 +#: src/view/com/util/forms/SearchInput.tsx:64 +#: src/view/shell/bottom-bar/BottomBar.tsx:132 +#: src/view/shell/desktop/Search.tsx:81 +#: src/view/shell/Drawer.tsx:218 +msgid "Search" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:110 +msgid "Security Step Required" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:30 +msgid "See what's next" +msgstr "" + +#: src/view/com/modals/ServerInput.tsx:75 +msgid "Select Bluesky Social" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:115 +msgid "Select from an existing account" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:141 +msgid "Select service" +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:191 +msgid "Send Confirmation Email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:119 +msgid "Send email" +msgstr "" + +#: src/view/com/modals/DeleteAccount.tsx:130 +msgid "Send Email" +msgstr "" + +#: src/view/shell/Drawer.tsx:349 +#: src/view/shell/Drawer.tsx:370 +msgid "Send feedback" +msgstr "" + +#: src/view/com/modals/report/SendReportButton.tsx:45 +msgid "Send Report" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:80 +msgid "Set new password" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:172 +msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:101 +msgid "Set this setting to \"No\" to hide all replies from your feed." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:155 +msgid "Set this setting to \"No\" to hide all reposts from your feed." +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:96 +msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:191 +msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." +msgstr "" + +#: src/view/shell/Drawer.tsx:338 +msgid "Settings" +msgstr "" + +#: src/view/com/modals/SelfLabel.tsx:126 +msgid "Sexual activity or erotic nudity." +msgstr "" + +#: src/view/screens/Settings.tsx:235 +msgid "Show" +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:114 +msgid "Show anyway" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:188 +msgid "Show Posts from My Feeds" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:169 +msgid "Show Quote Posts" +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:98 +msgid "Show Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:76 +msgid "Show replies by people you follow before all other replies." +msgstr "" + +#: src/view/screens/PreferencesHomeFeed.tsx:152 +msgid "Show Reposts" +msgstr "" + +#: src/view/com/notifications/FeedItem.tsx:337 +msgid "Show users" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:95 +#: src/view/com/auth/SplashScreen.tsx:50 +msgid "Sign in" +msgstr "" + +#: src/view/com/auth/SplashScreen.tsx:53 +#: src/view/com/auth/SplashScreen.web.tsx:59 +msgid "Sign In" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:65 +msgid "Sign in as {0}" +msgstr "" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:56 +#: src/view/com/auth/login/Login.tsx:114 +msgid "Sign in as..." +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:128 +msgid "Sign into" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:71 +#: src/view/com/modals/SwitchAccount.tsx:74 +#: src/view/screens/Settings.tsx:274 +#: src/view/screens/Settings.tsx:277 +msgid "Sign out" +msgstr "" + +#: src/view/screens/Settings.tsx:245 +msgid "Signed in as" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:37 +msgid "Skip" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:51 +msgid "Sort Replies" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:54 +msgid "Sort replies to the same post by:" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:122 +msgid "Square" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:106 +#: src/view/com/modals/ServerInput.tsx:62 +msgid "Staging" +msgstr "" + +#: src/view/screens/Settings.tsx:624 +msgid "Status page" +msgstr "" + +#: src/view/screens/Settings.tsx:582 +msgid "Storybook" +msgstr "" + +#: src/view/screens/ProfileList.tsx:533 +msgid "Subscribe" +msgstr "" + +#: src/view/screens/ProfileList.tsx:529 +msgid "Subscribe to this list" +msgstr "" + +#: src/view/com/modals/SwitchAccount.tsx:47 +msgid "Switch Account" +msgstr "" + +#: src/view/screens/Settings.tsx:562 +msgid "System log" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:112 +msgid "Tall" +msgstr "" + +#: src/view/com/modals/report/InputIssueDetails.tsx:50 +msgid "Text input field" +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:312 +msgid "The post may have been deleted." +msgstr "" + +#: src/view/com/util/moderation/ScreenHider.tsx:71 +msgid "This {screenDescription} has been flagged:" +msgstr "" + +#: src/view/com/modals/BirthDateSettings.tsx:58 +msgid "This information is not shared with other users." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:107 +msgid "This is important in case you ever need to change your email or reset your password." +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:71 +msgid "This is the service that keeps you online." +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:63 +msgid "This link is taking you to the following website:" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:176 +msgid "This post has been deleted." +msgstr "" + +#: src/view/screens/Settings.tsx:441 +msgid "Thread Preferences" +msgstr "" + +#: src/view/screens/PreferencesThreads.tsx:93 +msgid "Threaded Mode" +msgstr "" + +#: src/view/com/util/forms/DropdownButton.tsx:230 +msgid "Toggle dropdown" +msgstr "" + +#: src/view/com/modals/EditImage.tsx:271 +msgid "Transformations" +msgstr "" + +#: src/view/com/post-thread/PostThreadItem.tsx:654 +msgid "Translate" +msgstr "" + +#: src/view/com/util/error/ErrorScreen.tsx:73 +msgid "Try again" +msgstr "" + +#: src/view/com/auth/login/Login.tsx:74 +#: src/view/com/auth/login/LoginForm.tsx:113 +msgid "Unable to contact your service. Please check your Internet connection." +msgstr "" + +#: src/view/com/profile/ProfileHeader.tsx:386 +#: src/view/com/profile/ProfileHeader.tsx:389 +msgid "Unblock" +msgstr "" + +#: src/view/com/modals/UserAddRemoveLists.tsx:185 +msgid "Update {displayName} in Lists" +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:174 +msgid "Updating..." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:456 +msgid "Upload a text file to:" +msgstr "" + +#: src/view/screens/AppPasswords.tsx:148 +msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." +msgstr "" + +#: src/view/com/modals/ChangeHandle.tsx:516 +msgid "Use default provider" +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:37 +msgid "User handle" +msgstr "" + +#: src/view/com/auth/login/LoginForm.tsx:168 +#: src/view/com/auth/login/LoginForm.tsx:185 +msgid "Username or email address" +msgstr "" + +#: src/view/screens/ProfileList.tsx:694 +msgid "Users" +msgstr "" + +#: src/view/screens/Settings.tsx:650 +msgid "Verify email" +msgstr "" + +#: src/view/screens/Settings.tsx:675 +msgid "Verify my email" +msgstr "" + +#: src/view/screens/Settings.tsx:684 +msgid "Verify My Email" +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:205 +#: src/view/com/modals/ChangeEmail.tsx:207 +msgid "Verify New Email" +msgstr "" + +#: src/view/screens/Log.tsx:53 +msgid "View debug entry" +msgstr "" + +#: src/view/com/profile/ProfileSubpageHeader.tsx:127 +msgid "View the avatar" +msgstr "" + +#: src/view/com/modals/LinkWarning.tsx:80 +msgid "Visit Site" +msgstr "" + +#: src/view/com/auth/create/CreateAccount.tsx:78 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/view/com/modals/report/Modal.tsx:168 +msgid "What is the issue with this {collectionName}?" +msgstr "" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:72 +msgid "Which languages are used in this post?" +msgstr "" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:71 +msgid "Which languages would you like to see in your algorithmic feeds?" +msgstr "" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:102 +msgid "Wide" +msgstr "" + +#: src/view/com/composer/Composer.tsx:390 +msgid "Write post" +msgstr "" + +#: src/view/com/composer/Prompt.tsx:31 +msgid "Write your reply" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:122 +msgid "You can change hosting providers at any time." +msgstr "" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:31 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/view/com/post-thread/PostThread.tsx:346 +msgid "You have blocked the author or you have been blocked by the author." +msgstr "" + +#: src/view/com/lists/ListsList.tsx:112 +msgid "You have no lists." +msgstr "" + +#: src/view/screens/AppPasswords.tsx:55 +msgid "You have not created any app passwords yet. You can create one by pressing the button below." +msgstr "" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:83 +msgid "You will receive an email with a \"reset code.\" Enter that code here, then enter your new password." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:42 +msgid "Your account" +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:121 +msgid "Your birth date" +msgstr "" + +#: src/view/com/modals/Waitlist.tsx:107 +msgid "Your email has been saved! We'll be in touch soon." +msgstr "" + +#: src/view/com/modals/ChangeEmail.tsx:125 +msgid "Your email has been updated but not verified. As a next step, please verify your new email." +msgstr "" + +#: src/view/com/modals/VerifyEmail.tsx:102 +msgid "Your email has not yet been verified. This is an important security step which we recommend." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:41 +#: src/view/com/modals/ChangeHandle.tsx:275 +msgid "Your full handle will be" +msgstr "" + +#: src/view/com/auth/create/Step1.tsx:69 +msgid "Your hosting provider" +msgstr "" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:63 +msgid "Your posts, likes, and blocks are public. Mutes are private." +msgstr "" + +#: src/view/com/auth/create/Step3.tsx:27 +msgid "Your user handle" +msgstr "" diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx index 67453f111..05e72a2e6 100644 --- a/src/view/com/auth/SplashScreen.tsx +++ b/src/view/com/auth/SplashScreen.tsx @@ -5,6 +5,8 @@ import {ErrorBoundary} from 'view/com/util/ErrorBoundary' import {s, colors} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {CenteredView} from '../util/Views' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export const SplashScreen = ({ onPressSignin, @@ -14,14 +16,18 @@ export const SplashScreen = ({ onPressCreateAccount: () => void }) => { const pal = usePalette('default') + const {_} = useLingui() + return ( <CenteredView style={[styles.container, pal.view]}> <SafeAreaView testID="noSessionView" style={styles.container}> <ErrorBoundary> <View style={styles.hero}> - <Text style={[styles.title, pal.link]}>Bluesky</Text> + <Text style={[styles.title, pal.link]}> + <Trans>Bluesky</Trans> + </Text> <Text style={[styles.subtitle, pal.textLight]}> - See what's next + <Trans>See what's next</Trans> </Text> </View> <View testID="signinOrCreateAccount" style={styles.btns}> @@ -30,10 +36,10 @@ export const SplashScreen = ({ style={[styles.btn, {backgroundColor: colors.blue3}]} onPress={onPressCreateAccount} accessibilityRole="button" - accessibilityLabel="Create new account" + accessibilityLabel={_(msg`Create new account`)} accessibilityHint="Opens flow to create a new Bluesky account"> <Text style={[s.white, styles.btnLabel]}> - Create a new account + <Trans>Create a new account</Trans> </Text> </TouchableOpacity> <TouchableOpacity @@ -41,9 +47,11 @@ export const SplashScreen = ({ style={[styles.btn, pal.btn]} onPress={onPressSignin} accessibilityRole="button" - accessibilityLabel="Sign in" + accessibilityLabel={_(msg`Sign in`)} accessibilityHint="Opens flow to sign into your existing Bluesky account"> - <Text style={[pal.text, styles.btnLabel]}>Sign In</Text> + <Text style={[pal.text, styles.btnLabel]}> + <Trans>Sign In</Trans> + </Text> </TouchableOpacity> </View> </ErrorBoundary> diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx index cef9618ef..f10dc4f98 100644 --- a/src/view/com/auth/SplashScreen.web.tsx +++ b/src/view/com/auth/SplashScreen.web.tsx @@ -8,6 +8,7 @@ import {usePalette} from 'lib/hooks/usePalette' import {CenteredView} from '../util/Views' import {isWeb} from 'platform/detection' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Trans} from '@lingui/macro' export const SplashScreen = ({ onPressSignin, @@ -54,7 +55,9 @@ export const SplashScreen = ({ onPress={onPressSignin} // TODO: web accessibility accessibilityRole="button"> - <Text style={[pal.text, styles.btnLabel]}>Sign In</Text> + <Text style={[pal.text, styles.btnLabel]}> + <Trans>Sign In</Trans> + </Text> </TouchableOpacity> </View> </ErrorBoundary> diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx index c3cfb3ad3..8e2bbed85 100644 --- a/src/view/com/auth/create/CreateAccount.tsx +++ b/src/view/com/auth/create/CreateAccount.tsx @@ -15,6 +15,8 @@ import {s} from 'lib/styles' import {useStores} from 'state/index' import {CreateAccountModel} from 'state/models/ui/create-account' import {usePalette} from 'lib/hooks/usePalette' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useOnboardingDispatch} from '#/state/shell' import {Step1} from './Step1' @@ -30,6 +32,7 @@ export const CreateAccount = observer(function CreateAccountImpl({ const pal = usePalette('default') const store = useStores() const model = React.useMemo(() => new CreateAccountModel(store), [store]) + const {_} = useLingui() const onboardingDispatch = useOnboardingDispatch() React.useEffect(() => { @@ -73,8 +76,8 @@ export const CreateAccount = observer(function CreateAccountImpl({ return ( <LoggedOutLayout leadin={`Step ${model.step}`} - title="Create Account" - description="We're so excited to have you join us!"> + title={_(msg`Create Account`)} + description={_(msg`We're so excited to have you join us!`)}> <ScrollView testID="createAccount" style={pal.view}> <KeyboardAvoidingView behavior="padding"> <View style={styles.stepContainer}> @@ -88,7 +91,7 @@ export const CreateAccount = observer(function CreateAccountImpl({ testID="backBtn" accessibilityRole="button"> <Text type="xl" style={pal.link}> - Back + <Trans>Back</Trans> </Text> </TouchableOpacity> <View style={s.flex1} /> @@ -101,7 +104,7 @@ export const CreateAccount = observer(function CreateAccountImpl({ <ActivityIndicator /> ) : ( <Text type="xl-bold" style={[pal.link, s.pr5]}> - Next + <Trans>Next</Trans> </Text> )} </TouchableOpacity> @@ -110,18 +113,18 @@ export const CreateAccount = observer(function CreateAccountImpl({ testID="retryConnectBtn" onPress={onPressRetryConnect} accessibilityRole="button" - accessibilityLabel="Retry" + accessibilityLabel={_(msg`Retry`)} accessibilityHint="Retries account creation" accessibilityLiveRegion="polite"> <Text type="xl-bold" style={[pal.link, s.pr5]}> - Retry + <Trans>Retry</Trans> </Text> </TouchableOpacity> ) : model.isFetchingServiceDescription ? ( <> <ActivityIndicator color="#fff" /> <Text type="xl" style={[pal.text, s.pr5]}> - Connecting... + <Trans>Connecting...</Trans> </Text> </> ) : undefined} diff --git a/src/view/com/auth/create/Step1.tsx b/src/view/com/auth/create/Step1.tsx index cdd5cb21d..7e3ea062d 100644 --- a/src/view/com/auth/create/Step1.tsx +++ b/src/view/com/auth/create/Step1.tsx @@ -12,6 +12,8 @@ import {HelpTip} from '../util/HelpTip' import {TextInput} from '../util/TextInput' import {Button} from 'view/com/util/forms/Button' import {ErrorMessage} from 'view/com/util/error/ErrorMessage' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'state/index' import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags' @@ -27,6 +29,7 @@ export const Step1 = observer(function Step1Impl({ }) { const pal = usePalette('default') const [isDefaultSelected, setIsDefaultSelected] = React.useState(true) + const {_} = useLingui() const onPressDefault = React.useCallback(() => { setIsDefaultSelected(true) @@ -63,9 +66,9 @@ export const Step1 = observer(function Step1Impl({ return ( <View> - <StepHeader step="1" title="Your hosting provider" /> + <StepHeader step="1" title={_(msg`Your hosting provider`)} /> <Text style={[pal.text, s.mb10]}> - This is the service that keeps you online. + <Trans>This is the service that keeps you online.</Trans> </Text> <Option testID="blueskyServerBtn" @@ -81,17 +84,17 @@ export const Step1 = observer(function Step1Impl({ onPress={onPressOther}> <View style={styles.otherForm}> <Text nativeID="addressProvider" style={[pal.text, s.mb5]}> - Enter the address of your provider: + <Trans>Enter the address of your provider:</Trans> </Text> <TextInput testID="customServerInput" icon="globe" - placeholder="Hosting provider address" + placeholder={_(msg`Hosting provider address`)} value={model.serviceUrl} editable onChange={onChangeServiceUrl} accessibilityHint="Input hosting provider address" - accessibilityLabel="Hosting provider address" + accessibilityLabel={_(msg`Hosting provider address`)} accessibilityLabelledBy="addressProvider" /> {LOGIN_INCLUDE_DEV_SERVERS && ( @@ -100,13 +103,13 @@ export const Step1 = observer(function Step1Impl({ testID="stagingServerBtn" type="default" style={s.mr5} - label="Staging" + label={_(msg`Staging`)} onPress={() => onDebugChangeServiceUrl(STAGING_SERVICE)} /> <Button testID="localDevServerBtn" type="default" - label="Dev Server" + label={_(msg`Dev Server`)} onPress={() => onDebugChangeServiceUrl(LOCAL_DEV_SERVICE)} /> </View> @@ -116,7 +119,7 @@ export const Step1 = observer(function Step1Impl({ {model.error ? ( <ErrorMessage message={model.error} style={styles.error} /> ) : ( - <HelpTip text="You can change hosting providers at any time." /> + <HelpTip text={_(msg`You can change hosting providers at any time.`)} /> )} </View> ) diff --git a/src/view/com/auth/create/Step2.tsx b/src/view/com/auth/create/Step2.tsx index b2054150b..3cc8ae934 100644 --- a/src/view/com/auth/create/Step2.tsx +++ b/src/view/com/auth/create/Step2.tsx @@ -11,6 +11,8 @@ import {TextInput} from '../util/TextInput' import {Policies} from './Policies' import {ErrorMessage} from 'view/com/util/error/ErrorMessage' import {isWeb} from 'platform/detection' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' /** STEP 2: Your account @@ -28,6 +30,7 @@ export const Step2 = observer(function Step2Impl({ model: CreateAccountModel }) { const pal = usePalette('default') + const {_} = useLingui() const {openModal} = useModalControls() const onPressWaitlist = React.useCallback(() => { @@ -36,7 +39,7 @@ export const Step2 = observer(function Step2Impl({ return ( <View> - <StepHeader step="2" title="Your account" /> + <StepHeader step="2" title={_(msg`Your account`)} /> {model.isInviteCodeRequired && ( <View style={s.pb20}> @@ -46,11 +49,11 @@ export const Step2 = observer(function Step2Impl({ <TextInput testID="inviteCodeInput" icon="ticket" - placeholder="Required for this provider" + placeholder={_(msg`Required for this provider`)} value={model.inviteCode} editable onChange={model.setInviteCode} - accessibilityLabel="Invite code" + accessibilityLabel={_(msg`Invite code`)} accessibilityHint="Input invite code to proceed" /> </View> @@ -61,10 +64,12 @@ export const Step2 = observer(function Step2Impl({ Don't have an invite code?{' '} <TouchableWithoutFeedback onPress={onPressWaitlist} - accessibilityLabel="Join the waitlist." + accessibilityLabel={_(msg`Join the waitlist.`)} accessibilityHint=""> <View style={styles.touchable}> - <Text style={pal.link}>Join the waitlist.</Text> + <Text style={pal.link}> + <Trans>Join the waitlist.</Trans> + </Text> </View> </TouchableWithoutFeedback> </Text> @@ -72,16 +77,16 @@ export const Step2 = observer(function Step2Impl({ <> <View style={s.pb20}> <Text type="md-medium" style={[pal.text, s.mb2]} nativeID="email"> - Email address + <Trans>Email address</Trans> </Text> <TextInput testID="emailInput" icon="envelope" - placeholder="Enter your email address" + placeholder={_(msg`Enter your email address`)} value={model.email} editable onChange={model.setEmail} - accessibilityLabel="Email" + accessibilityLabel={_(msg`Email`)} accessibilityHint="Input email for Bluesky waitlist" accessibilityLabelledBy="email" /> @@ -92,17 +97,17 @@ export const Step2 = observer(function Step2Impl({ type="md-medium" style={[pal.text, s.mb2]} nativeID="password"> - Password + <Trans>Password</Trans> </Text> <TextInput testID="passwordInput" icon="lock" - placeholder="Choose your password" + placeholder={_(msg`Choose your password`)} value={model.password} editable secureTextEntry onChange={model.setPassword} - accessibilityLabel="Password" + accessibilityLabel={_(msg`Password`)} accessibilityHint="Set password" accessibilityLabelledBy="password" /> @@ -113,7 +118,7 @@ export const Step2 = observer(function Step2Impl({ type="md-medium" style={[pal.text, s.mb2]} nativeID="birthDate"> - Your birth date + <Trans>Your birth date</Trans> </Text> <DateInput testID="birthdayInput" @@ -122,7 +127,7 @@ export const Step2 = observer(function Step2Impl({ buttonType="default-light" buttonStyle={[pal.border, styles.dateInputButton]} buttonLabelType="lg" - accessibilityLabel="Birthday" + accessibilityLabel={_(msg`Birthday`)} accessibilityHint="Enter your birth date" accessibilityLabelledBy="birthDate" /> diff --git a/src/view/com/auth/create/Step3.tsx b/src/view/com/auth/create/Step3.tsx index beb756ac1..09fba0714 100644 --- a/src/view/com/auth/create/Step3.tsx +++ b/src/view/com/auth/create/Step3.tsx @@ -9,6 +9,8 @@ import {TextInput} from '../util/TextInput' import {createFullHandle} from 'lib/strings/handles' import {usePalette} from 'lib/hooks/usePalette' import {ErrorMessage} from 'view/com/util/error/ErrorMessage' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' /** STEP 3: Your user handle * @field User handle @@ -19,9 +21,10 @@ export const Step3 = observer(function Step3Impl({ model: CreateAccountModel }) { const pal = usePalette('default') + const {_} = useLingui() return ( <View> - <StepHeader step="3" title="Your user handle" /> + <StepHeader step="3" title={_(msg`Your user handle`)} /> <View style={s.pb10}> <TextInput testID="handleInput" @@ -31,12 +34,12 @@ export const Step3 = observer(function Step3Impl({ editable onChange={model.setHandle} // TODO: Add explicit text label - accessibilityLabel="User handle" + accessibilityLabel={_(msg`User handle`)} accessibilityHint="Input your user handle" /> <Text type="lg" style={[pal.text, s.pl5, s.pt10]}> - Your full handle will be{' '} - <Text type="lg-bold" style={pal.text}> + <Trans>Your full handle will be</Trans> + <Text type="lg-bold" style={[pal.text, s.ml5]}> @{createFullHandle(model.handle, model.userDomain)} </Text> </Text> diff --git a/src/view/com/auth/login/ChooseAccountForm.tsx b/src/view/com/auth/login/ChooseAccountForm.tsx new file mode 100644 index 000000000..596a8e411 --- /dev/null +++ b/src/view/com/auth/login/ChooseAccountForm.tsx @@ -0,0 +1,119 @@ +import React from 'react' +import { + ActivityIndicator, + ScrollView, + TouchableOpacity, + View, +} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {useAnalytics} from 'lib/analytics/analytics' +import {Text} from '../../util/text/Text' +import {UserAvatar} from '../../util/UserAvatar' +import {s} from 'lib/styles' +import {RootStoreModel} from 'state/index' +import {AccountData} from 'state/models/session' +import {usePalette} from 'lib/hooks/usePalette' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {styles} from './styles' + +export const ChooseAccountForm = ({ + store, + onSelectAccount, + onPressBack, +}: { + store: RootStoreModel + onSelectAccount: (account?: AccountData) => void + onPressBack: () => void +}) => { + const {track, screen} = useAnalytics() + const pal = usePalette('default') + const [isProcessing, setIsProcessing] = React.useState(false) + const {_} = useLingui() + + React.useEffect(() => { + screen('Choose Account') + }, [screen]) + + const onTryAccount = async (account: AccountData) => { + if (account.accessJwt && account.refreshJwt) { + setIsProcessing(true) + if (await store.session.resumeSession(account)) { + track('Sign In', {resumedSession: true}) + setIsProcessing(false) + return + } + setIsProcessing(false) + } + onSelectAccount(account) + } + + return ( + <ScrollView testID="chooseAccountForm" style={styles.maxHeight}> + <Text + type="2xl-medium" + style={[pal.text, styles.groupLabel, s.mt5, s.mb10]}> + <Trans>Sign in as...</Trans> + </Text> + {store.session.accounts.map(account => ( + <TouchableOpacity + testID={`chooseAccountBtn-${account.handle}`} + key={account.did} + style={[pal.view, pal.border, styles.account]} + onPress={() => onTryAccount(account)} + accessibilityRole="button" + accessibilityLabel={_(msg`Sign in as ${account.handle}`)} + accessibilityHint="Double tap to sign in"> + <View + style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> + <View style={s.p10}> + <UserAvatar avatar={account.aviUrl} size={30} /> + </View> + <Text style={styles.accountText}> + <Text type="lg-bold" style={pal.text}> + {account.displayName || account.handle}{' '} + </Text> + <Text type="lg" style={[pal.textLight]}> + {account.handle} + </Text> + </Text> + <FontAwesomeIcon + icon="angle-right" + size={16} + style={[pal.text, s.mr10]} + /> + </View> + </TouchableOpacity> + ))} + <TouchableOpacity + testID="chooseNewAccountBtn" + style={[pal.view, pal.border, styles.account, styles.accountLast]} + onPress={() => onSelectAccount(undefined)} + accessibilityRole="button" + accessibilityLabel={_(msg`Login to account that is not listed`)} + accessibilityHint=""> + <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> + <Text style={[styles.accountText, styles.accountTextOther]}> + <Text type="lg" style={pal.text}> + <Trans>Other account</Trans> + </Text> + </Text> + <FontAwesomeIcon + icon="angle-right" + size={16} + style={[pal.text, s.mr10]} + /> + </View> + </TouchableOpacity> + <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> + <TouchableOpacity onPress={onPressBack} accessibilityRole="button"> + <Text type="xl" style={[pal.link, s.pl5]}> + <Trans>Back</Trans> + </Text> + </TouchableOpacity> + <View style={s.flex1} /> + {isProcessing && <ActivityIndicator />} + </View> + </ScrollView> + ) +} diff --git a/src/view/com/auth/login/ForgotPasswordForm.tsx b/src/view/com/auth/login/ForgotPasswordForm.tsx new file mode 100644 index 000000000..9bfab18b5 --- /dev/null +++ b/src/view/com/auth/login/ForgotPasswordForm.tsx @@ -0,0 +1,197 @@ +import React, {useState, useEffect} from 'react' +import { + ActivityIndicator, + TextInput, + TouchableOpacity, + View, +} from 'react-native' +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' +import * as EmailValidator from 'email-validator' +import {BskyAgent} from '@atproto/api' +import {useAnalytics} from 'lib/analytics/analytics' +import {Text} from '../../util/text/Text' +import {s} from 'lib/styles' +import {toNiceDomain} from 'lib/strings/url-helpers' +import {RootStoreModel} from 'state/index' +import {ServiceDescription} from 'state/models/session' +import {isNetworkError} from 'lib/strings/errors' +import {usePalette} from 'lib/hooks/usePalette' +import {useTheme} from 'lib/ThemeContext' +import {cleanError} from 'lib/strings/errors' +import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {styles} from './styles' +import {useModalControls} from '#/state/modals' + +export const ForgotPasswordForm = ({ + error, + serviceUrl, + serviceDescription, + setError, + setServiceUrl, + onPressBack, + onEmailSent, +}: { + store: RootStoreModel + error: string + serviceUrl: string + serviceDescription: ServiceDescription | undefined + setError: (v: string) => void + setServiceUrl: (v: string) => void + onPressBack: () => void + onEmailSent: () => void +}) => { + const pal = usePalette('default') + const theme = useTheme() + const [isProcessing, setIsProcessing] = useState<boolean>(false) + const [email, setEmail] = useState<string>('') + const {screen} = useAnalytics() + const {_} = useLingui() + const {openModal} = useModalControls() + + useEffect(() => { + screen('Signin:ForgotPassword') + }, [screen]) + + const onPressSelectService = () => { + openModal({ + name: 'server-input', + initialService: serviceUrl, + onSelect: setServiceUrl, + }) + } + + const onPressNext = async () => { + if (!EmailValidator.validate(email)) { + return setError('Your email appears to be invalid.') + } + + setError('') + setIsProcessing(true) + + try { + const agent = new BskyAgent({service: serviceUrl}) + await agent.com.atproto.server.requestPasswordReset({email}) + onEmailSent() + } catch (e: any) { + const errMsg = e.toString() + logger.warn('Failed to request password reset', {error: e}) + setIsProcessing(false) + if (isNetworkError(e)) { + setError( + 'Unable to contact your service. Please check your Internet connection.', + ) + } else { + setError(cleanError(errMsg)) + } + } + } + + return ( + <> + <View> + <Text type="title-lg" style={[pal.text, styles.screenTitle]}> + <Trans>Reset password</Trans> + </Text> + <Text type="md" style={[pal.text, styles.instructions]}> + <Trans> + Enter the email you used to create your account. We'll send you a + "reset code" so you can set a new password. + </Trans> + </Text> + <View + testID="forgotPasswordView" + style={[pal.borderDark, pal.view, styles.group]}> + <TouchableOpacity + testID="forgotPasswordSelectServiceButton" + style={[pal.borderDark, styles.groupContent, styles.noTopBorder]} + onPress={onPressSelectService} + accessibilityRole="button" + accessibilityLabel={_(msg`Hosting provider`)} + accessibilityHint="Sets hosting provider for password reset"> + <FontAwesomeIcon + icon="globe" + style={[pal.textLight, styles.groupContentIcon]} + /> + <Text style={[pal.text, styles.textInput]} numberOfLines={1}> + {toNiceDomain(serviceUrl)} + </Text> + <View style={[pal.btn, styles.textBtnFakeInnerBtn]}> + <FontAwesomeIcon + icon="pen" + size={12} + style={pal.text as FontAwesomeIconStyle} + /> + </View> + </TouchableOpacity> + <View style={[pal.borderDark, styles.groupContent]}> + <FontAwesomeIcon + icon="envelope" + style={[pal.textLight, styles.groupContentIcon]} + /> + <TextInput + testID="forgotPasswordEmail" + style={[pal.text, styles.textInput]} + placeholder="Email address" + placeholderTextColor={pal.colors.textLight} + autoCapitalize="none" + autoFocus + autoCorrect={false} + keyboardAppearance={theme.colorScheme} + value={email} + onChangeText={setEmail} + editable={!isProcessing} + accessibilityLabel={_(msg`Email`)} + accessibilityHint="Sets email for password reset" + /> + </View> + </View> + {error ? ( + <View style={styles.error}> + <View style={styles.errorIcon}> + <FontAwesomeIcon icon="exclamation" style={s.white} size={10} /> + </View> + <View style={s.flex1}> + <Text style={[s.white, s.bold]}>{error}</Text> + </View> + </View> + ) : undefined} + <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> + <TouchableOpacity onPress={onPressBack} accessibilityRole="button"> + <Text type="xl" style={[pal.link, s.pl5]}> + <Trans>Back</Trans> + </Text> + </TouchableOpacity> + <View style={s.flex1} /> + {!serviceDescription || isProcessing ? ( + <ActivityIndicator /> + ) : !email ? ( + <Text type="xl-bold" style={[pal.link, s.pr5, styles.dimmed]}> + <Trans>Next</Trans> + </Text> + ) : ( + <TouchableOpacity + testID="newPasswordButton" + onPress={onPressNext} + accessibilityRole="button" + accessibilityLabel={_(msg`Go to next`)} + accessibilityHint="Navigates to the next screen"> + <Text type="xl-bold" style={[pal.link, s.pr5]}> + <Trans>Next</Trans> + </Text> + </TouchableOpacity> + )} + {!serviceDescription || isProcessing ? ( + <Text type="xl" style={[pal.textLight, s.pl10]}> + <Trans>Processing...</Trans> + </Text> + ) : undefined} + </View> + </View> + </> + ) +} diff --git a/src/view/com/auth/login/Login.tsx b/src/view/com/auth/login/Login.tsx index 24a657c66..401b7d980 100644 --- a/src/view/com/auth/login/Login.tsx +++ b/src/view/com/auth/login/Login.tsx @@ -1,37 +1,19 @@ -import React, {useState, useEffect, useRef} from 'react' -import { - ActivityIndicator, - Keyboard, - KeyboardAvoidingView, - ScrollView, - StyleSheet, - TextInput, - TouchableOpacity, - View, -} from 'react-native' -import { - FontAwesomeIcon, - FontAwesomeIconStyle, -} from '@fortawesome/react-native-fontawesome' -import * as EmailValidator from 'email-validator' -import {BskyAgent} from '@atproto/api' +import React, {useState, useEffect} from 'react' +import {KeyboardAvoidingView} from 'react-native' import {useAnalytics} from 'lib/analytics/analytics' -import {Text} from '../../util/text/Text' -import {UserAvatar} from '../../util/UserAvatar' import {LoggedOutLayout} from 'view/com/util/layouts/LoggedOutLayout' -import {s, colors} from 'lib/styles' -import {createFullHandle} from 'lib/strings/handles' -import {toNiceDomain} from 'lib/strings/url-helpers' -import {useStores, RootStoreModel, DEFAULT_SERVICE} from 'state/index' +import {useStores, DEFAULT_SERVICE} from 'state/index' import {ServiceDescription} from 'state/models/session' import {AccountData} from 'state/models/session' -import {isNetworkError} from 'lib/strings/errors' import {usePalette} from 'lib/hooks/usePalette' -import {useTheme} from 'lib/ThemeContext' -import {cleanError} from 'lib/strings/errors' -import {isWeb} from 'platform/detection' import {logger} from '#/logger' -import {useModalControls} from '#/state/modals' +import {ChooseAccountForm} from './ChooseAccountForm' +import {LoginForm} from './LoginForm' +import {ForgotPasswordForm} from './ForgotPasswordForm' +import {SetNewPasswordForm} from './SetNewPasswordForm' +import {PasswordUpdatedForm} from './PasswordUpdatedForm' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' enum Forms { Login, @@ -45,6 +27,7 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { const pal = usePalette('default') const store = useStores() const {track} = useAnalytics() + const {_} = useLingui() const [error, setError] = useState<string>('') const [retryDescribeTrigger, setRetryDescribeTrigger] = useState<any>({}) const [serviceUrl, setServiceUrl] = useState<string>(DEFAULT_SERVICE) @@ -87,14 +70,16 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { error: err, }) setError( - 'Unable to contact your service. Please check your Internet connection.', + _( + msg`Unable to contact your service. Please check your Internet connection.`, + ), ) }, ) return () => { aborted = true } - }, [store.session, serviceUrl, retryDescribeTrigger]) + }, [store.session, serviceUrl, retryDescribeTrigger, _]) const onPressRetryConnect = () => setRetryDescribeTrigger({}) const onPressForgotPassword = () => { @@ -107,8 +92,8 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { {currentForm === Forms.Login ? ( <LoggedOutLayout leadin="" - title="Sign in" - description="Enter your username and password"> + title={_(msg`Sign in`)} + description={_(msg`Enter your username and password`)}> <LoginForm store={store} error={error} @@ -126,8 +111,8 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { {currentForm === Forms.ChooseAccount ? ( <LoggedOutLayout leadin="" - title="Sign in as..." - description="Select from an existing account"> + title={_(msg`Sign in as...`)} + description={_(msg`Select from an existing account`)}> <ChooseAccountForm store={store} onSelectAccount={onSelectAccount} @@ -138,8 +123,8 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { {currentForm === Forms.ForgotPassword ? ( <LoggedOutLayout leadin="" - title="Forgot Password" - description="Let's get your password reset!"> + title={_(msg`Forgot Password`)} + description={_(msg`Let's get your password reset!`)}> <ForgotPasswordForm store={store} error={error} @@ -155,8 +140,8 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { {currentForm === Forms.SetNewPassword ? ( <LoggedOutLayout leadin="" - title="Forgot Password" - description="Let's get your password reset!"> + title={_(msg`Forgot Password`)} + description={_(msg`Let's get your password reset!`)}> <SetNewPasswordForm store={store} error={error} @@ -173,830 +158,3 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => { </KeyboardAvoidingView> ) } - -const ChooseAccountForm = ({ - store, - onSelectAccount, - onPressBack, -}: { - store: RootStoreModel - onSelectAccount: (account?: AccountData) => void - onPressBack: () => void -}) => { - const {track, screen} = useAnalytics() - const pal = usePalette('default') - const [isProcessing, setIsProcessing] = React.useState(false) - - React.useEffect(() => { - screen('Choose Account') - }, [screen]) - - const onTryAccount = async (account: AccountData) => { - if (account.accessJwt && account.refreshJwt) { - setIsProcessing(true) - if (await store.session.resumeSession(account)) { - track('Sign In', {resumedSession: true}) - setIsProcessing(false) - return - } - setIsProcessing(false) - } - onSelectAccount(account) - } - - return ( - <ScrollView testID="chooseAccountForm" style={styles.maxHeight}> - <Text - type="2xl-medium" - style={[pal.text, styles.groupLabel, s.mt5, s.mb10]}> - Sign in as... - </Text> - {store.session.accounts.map(account => ( - <TouchableOpacity - testID={`chooseAccountBtn-${account.handle}`} - key={account.did} - style={[pal.view, pal.border, styles.account]} - onPress={() => onTryAccount(account)} - accessibilityRole="button" - accessibilityLabel={`Sign in as ${account.handle}`} - accessibilityHint="Double tap to sign in"> - <View - style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> - <View style={s.p10}> - <UserAvatar avatar={account.aviUrl} size={30} /> - </View> - <Text style={styles.accountText}> - <Text type="lg-bold" style={pal.text}> - {account.displayName || account.handle}{' '} - </Text> - <Text type="lg" style={[pal.textLight]}> - {account.handle} - </Text> - </Text> - <FontAwesomeIcon - icon="angle-right" - size={16} - style={[pal.text, s.mr10]} - /> - </View> - </TouchableOpacity> - ))} - <TouchableOpacity - testID="chooseNewAccountBtn" - style={[pal.view, pal.border, styles.account, styles.accountLast]} - onPress={() => onSelectAccount(undefined)} - accessibilityRole="button" - accessibilityLabel="Login to account that is not listed" - accessibilityHint=""> - <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> - <Text style={[styles.accountText, styles.accountTextOther]}> - <Text type="lg" style={pal.text}> - Other account - </Text> - </Text> - <FontAwesomeIcon - icon="angle-right" - size={16} - style={[pal.text, s.mr10]} - /> - </View> - </TouchableOpacity> - <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> - <TouchableOpacity onPress={onPressBack} accessibilityRole="button"> - <Text type="xl" style={[pal.link, s.pl5]}> - Back - </Text> - </TouchableOpacity> - <View style={s.flex1} /> - {isProcessing && <ActivityIndicator />} - </View> - </ScrollView> - ) -} - -const LoginForm = ({ - store, - error, - serviceUrl, - serviceDescription, - initialHandle, - setError, - setServiceUrl, - onPressRetryConnect, - onPressBack, - onPressForgotPassword, -}: { - store: RootStoreModel - error: string - serviceUrl: string - serviceDescription: ServiceDescription | undefined - initialHandle: string - setError: (v: string) => void - setServiceUrl: (v: string) => void - onPressRetryConnect: () => void - onPressBack: () => void - onPressForgotPassword: () => void -}) => { - const {track} = useAnalytics() - const pal = usePalette('default') - const theme = useTheme() - const [isProcessing, setIsProcessing] = useState<boolean>(false) - const [identifier, setIdentifier] = useState<string>(initialHandle) - const [password, setPassword] = useState<string>('') - const passwordInputRef = useRef<TextInput>(null) - const {openModal} = useModalControls() - - const onPressSelectService = () => { - openModal({ - name: 'server-input', - initialService: serviceUrl, - onSelect: setServiceUrl, - }) - Keyboard.dismiss() - track('Signin:PressedSelectService') - } - - const onPressNext = async () => { - Keyboard.dismiss() - setError('') - setIsProcessing(true) - - try { - // try to guess the handle if the user just gave their own username - let fullIdent = identifier - if ( - !identifier.includes('@') && // not an email - !identifier.includes('.') && // not a domain - serviceDescription && - serviceDescription.availableUserDomains.length > 0 - ) { - let matched = false - for (const domain of serviceDescription.availableUserDomains) { - if (fullIdent.endsWith(domain)) { - matched = true - } - } - if (!matched) { - fullIdent = createFullHandle( - identifier, - serviceDescription.availableUserDomains[0], - ) - } - } - - await store.session.login({ - service: serviceUrl, - identifier: fullIdent, - password, - }) - } catch (e: any) { - const errMsg = e.toString() - logger.warn('Failed to login', {error: e}) - setIsProcessing(false) - if (errMsg.includes('Authentication Required')) { - setError('Invalid username or password') - } else if (isNetworkError(e)) { - setError( - 'Unable to contact your service. Please check your Internet connection.', - ) - } else { - setError(cleanError(errMsg)) - } - } finally { - track('Sign In', {resumedSession: false}) - } - } - - const isReady = !!serviceDescription && !!identifier && !!password - return ( - <View testID="loginForm"> - <Text type="sm-bold" style={[pal.text, styles.groupLabel]}> - Sign into - </Text> - <View style={[pal.borderDark, styles.group]}> - <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> - <FontAwesomeIcon - icon="globe" - style={[pal.textLight, styles.groupContentIcon]} - /> - <TouchableOpacity - testID="loginSelectServiceButton" - style={styles.textBtn} - onPress={onPressSelectService} - accessibilityRole="button" - accessibilityLabel="Select service" - accessibilityHint="Sets server for the Bluesky client"> - <Text type="xl" style={[pal.text, styles.textBtnLabel]}> - {toNiceDomain(serviceUrl)} - </Text> - <View style={[pal.btn, styles.textBtnFakeInnerBtn]}> - <FontAwesomeIcon - icon="pen" - size={12} - style={pal.textLight as FontAwesomeIconStyle} - /> - </View> - </TouchableOpacity> - </View> - </View> - <Text type="sm-bold" style={[pal.text, styles.groupLabel]}> - Account - </Text> - <View style={[pal.borderDark, styles.group]}> - <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> - <FontAwesomeIcon - icon="at" - style={[pal.textLight, styles.groupContentIcon]} - /> - <TextInput - testID="loginUsernameInput" - style={[pal.text, styles.textInput]} - placeholder="Username or email address" - placeholderTextColor={pal.colors.textLight} - autoCapitalize="none" - autoFocus - autoCorrect={false} - autoComplete="username" - returnKeyType="next" - onSubmitEditing={() => { - passwordInputRef.current?.focus() - }} - blurOnSubmit={false} // prevents flickering due to onSubmitEditing going to next field - keyboardAppearance={theme.colorScheme} - value={identifier} - onChangeText={str => - setIdentifier((str || '').toLowerCase().trim()) - } - editable={!isProcessing} - accessibilityLabel="Username or email address" - accessibilityHint="Input the username or email address you used at signup" - /> - </View> - <View style={[pal.borderDark, styles.groupContent]}> - <FontAwesomeIcon - icon="lock" - style={[pal.textLight, styles.groupContentIcon]} - /> - <TextInput - testID="loginPasswordInput" - ref={passwordInputRef} - style={[pal.text, styles.textInput]} - placeholder="Password" - placeholderTextColor={pal.colors.textLight} - autoCapitalize="none" - autoCorrect={false} - autoComplete="password" - returnKeyType="done" - enablesReturnKeyAutomatically={true} - keyboardAppearance={theme.colorScheme} - secureTextEntry={true} - textContentType="password" - clearButtonMode="while-editing" - value={password} - onChangeText={setPassword} - onSubmitEditing={onPressNext} - blurOnSubmit={false} // HACK: https://github.com/facebook/react-native/issues/21911#issuecomment-558343069 Keyboard blur behavior is now handled in onSubmitEditing - editable={!isProcessing} - accessibilityLabel="Password" - accessibilityHint={ - identifier === '' - ? 'Input your password' - : `Input the password tied to ${identifier}` - } - /> - <TouchableOpacity - testID="forgotPasswordButton" - style={styles.textInputInnerBtn} - onPress={onPressForgotPassword} - accessibilityRole="button" - accessibilityLabel="Forgot password" - accessibilityHint="Opens password reset form"> - <Text style={pal.link}>Forgot</Text> - </TouchableOpacity> - </View> - </View> - {error ? ( - <View style={styles.error}> - <View style={styles.errorIcon}> - <FontAwesomeIcon icon="exclamation" style={s.white} size={10} /> - </View> - <View style={s.flex1}> - <Text style={[s.white, s.bold]}>{error}</Text> - </View> - </View> - ) : undefined} - <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> - <TouchableOpacity onPress={onPressBack} accessibilityRole="button"> - <Text type="xl" style={[pal.link, s.pl5]}> - Back - </Text> - </TouchableOpacity> - <View style={s.flex1} /> - {!serviceDescription && error ? ( - <TouchableOpacity - testID="loginRetryButton" - onPress={onPressRetryConnect} - accessibilityRole="button" - accessibilityLabel="Retry" - accessibilityHint="Retries login"> - <Text type="xl-bold" style={[pal.link, s.pr5]}> - Retry - </Text> - </TouchableOpacity> - ) : !serviceDescription ? ( - <> - <ActivityIndicator /> - <Text type="xl" style={[pal.textLight, s.pl10]}> - Connecting... - </Text> - </> - ) : isProcessing ? ( - <ActivityIndicator /> - ) : isReady ? ( - <TouchableOpacity - testID="loginNextButton" - onPress={onPressNext} - accessibilityRole="button" - accessibilityLabel="Go to next" - accessibilityHint="Navigates to the next screen"> - <Text type="xl-bold" style={[pal.link, s.pr5]}> - Next - </Text> - </TouchableOpacity> - ) : undefined} - </View> - </View> - ) -} - -const ForgotPasswordForm = ({ - error, - serviceUrl, - serviceDescription, - setError, - setServiceUrl, - onPressBack, - onEmailSent, -}: { - store: RootStoreModel - error: string - serviceUrl: string - serviceDescription: ServiceDescription | undefined - setError: (v: string) => void - setServiceUrl: (v: string) => void - onPressBack: () => void - onEmailSent: () => void -}) => { - const pal = usePalette('default') - const theme = useTheme() - const [isProcessing, setIsProcessing] = useState<boolean>(false) - const [email, setEmail] = useState<string>('') - const {screen} = useAnalytics() - const {openModal} = useModalControls() - - useEffect(() => { - screen('Signin:ForgotPassword') - }, [screen]) - - const onPressSelectService = () => { - openModal({ - name: 'server-input', - initialService: serviceUrl, - onSelect: setServiceUrl, - }) - } - - const onPressNext = async () => { - if (!EmailValidator.validate(email)) { - return setError('Your email appears to be invalid.') - } - - setError('') - setIsProcessing(true) - - try { - const agent = new BskyAgent({service: serviceUrl}) - await agent.com.atproto.server.requestPasswordReset({email}) - onEmailSent() - } catch (e: any) { - const errMsg = e.toString() - logger.warn('Failed to request password reset', {error: e}) - setIsProcessing(false) - if (isNetworkError(e)) { - setError( - 'Unable to contact your service. Please check your Internet connection.', - ) - } else { - setError(cleanError(errMsg)) - } - } - } - - return ( - <> - <View> - <Text type="title-lg" style={[pal.text, styles.screenTitle]}> - Reset password - </Text> - <Text type="md" style={[pal.text, styles.instructions]}> - Enter the email you used to create your account. We'll send you a - "reset code" so you can set a new password. - </Text> - <View - testID="forgotPasswordView" - style={[pal.borderDark, pal.view, styles.group]}> - <TouchableOpacity - testID="forgotPasswordSelectServiceButton" - style={[pal.borderDark, styles.groupContent, styles.noTopBorder]} - onPress={onPressSelectService} - accessibilityRole="button" - accessibilityLabel="Hosting provider" - accessibilityHint="Sets hosting provider for password reset"> - <FontAwesomeIcon - icon="globe" - style={[pal.textLight, styles.groupContentIcon]} - /> - <Text style={[pal.text, styles.textInput]} numberOfLines={1}> - {toNiceDomain(serviceUrl)} - </Text> - <View style={[pal.btn, styles.textBtnFakeInnerBtn]}> - <FontAwesomeIcon - icon="pen" - size={12} - style={pal.text as FontAwesomeIconStyle} - /> - </View> - </TouchableOpacity> - <View style={[pal.borderDark, styles.groupContent]}> - <FontAwesomeIcon - icon="envelope" - style={[pal.textLight, styles.groupContentIcon]} - /> - <TextInput - testID="forgotPasswordEmail" - style={[pal.text, styles.textInput]} - placeholder="Email address" - placeholderTextColor={pal.colors.textLight} - autoCapitalize="none" - autoFocus - autoCorrect={false} - keyboardAppearance={theme.colorScheme} - value={email} - onChangeText={setEmail} - editable={!isProcessing} - accessibilityLabel="Email" - accessibilityHint="Sets email for password reset" - /> - </View> - </View> - {error ? ( - <View style={styles.error}> - <View style={styles.errorIcon}> - <FontAwesomeIcon icon="exclamation" style={s.white} size={10} /> - </View> - <View style={s.flex1}> - <Text style={[s.white, s.bold]}>{error}</Text> - </View> - </View> - ) : undefined} - <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> - <TouchableOpacity onPress={onPressBack} accessibilityRole="button"> - <Text type="xl" style={[pal.link, s.pl5]}> - Back - </Text> - </TouchableOpacity> - <View style={s.flex1} /> - {!serviceDescription || isProcessing ? ( - <ActivityIndicator /> - ) : !email ? ( - <Text type="xl-bold" style={[pal.link, s.pr5, styles.dimmed]}> - Next - </Text> - ) : ( - <TouchableOpacity - testID="newPasswordButton" - onPress={onPressNext} - accessibilityRole="button" - accessibilityLabel="Go to next" - accessibilityHint="Navigates to the next screen"> - <Text type="xl-bold" style={[pal.link, s.pr5]}> - Next - </Text> - </TouchableOpacity> - )} - {!serviceDescription || isProcessing ? ( - <Text type="xl" style={[pal.textLight, s.pl10]}> - Processing... - </Text> - ) : undefined} - </View> - </View> - </> - ) -} - -const SetNewPasswordForm = ({ - error, - serviceUrl, - setError, - onPressBack, - onPasswordSet, -}: { - store: RootStoreModel - error: string - serviceUrl: string - setError: (v: string) => void - onPressBack: () => void - onPasswordSet: () => void -}) => { - const pal = usePalette('default') - const theme = useTheme() - const {screen} = useAnalytics() - - useEffect(() => { - screen('Signin:SetNewPasswordForm') - }, [screen]) - - const [isProcessing, setIsProcessing] = useState<boolean>(false) - const [resetCode, setResetCode] = useState<string>('') - const [password, setPassword] = useState<string>('') - - const onPressNext = async () => { - setError('') - setIsProcessing(true) - - try { - const agent = new BskyAgent({service: serviceUrl}) - const token = resetCode.replace(/\s/g, '') - await agent.com.atproto.server.resetPassword({ - token, - password, - }) - onPasswordSet() - } catch (e: any) { - const errMsg = e.toString() - logger.warn('Failed to set new password', {error: e}) - setIsProcessing(false) - if (isNetworkError(e)) { - setError( - 'Unable to contact your service. Please check your Internet connection.', - ) - } else { - setError(cleanError(errMsg)) - } - } - } - - return ( - <> - <View> - <Text type="title-lg" style={[pal.text, styles.screenTitle]}> - Set new password - </Text> - <Text type="lg" style={[pal.text, styles.instructions]}> - You will receive an email with a "reset code." Enter that code here, - then enter your new password. - </Text> - <View - testID="newPasswordView" - style={[pal.view, pal.borderDark, styles.group]}> - <View - style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> - <FontAwesomeIcon - icon="ticket" - style={[pal.textLight, styles.groupContentIcon]} - /> - <TextInput - testID="resetCodeInput" - style={[pal.text, styles.textInput]} - placeholder="Reset code" - placeholderTextColor={pal.colors.textLight} - autoCapitalize="none" - autoCorrect={false} - keyboardAppearance={theme.colorScheme} - autoFocus - value={resetCode} - onChangeText={setResetCode} - editable={!isProcessing} - accessible={true} - accessibilityLabel="Reset code" - accessibilityHint="Input code sent to your email for password reset" - /> - </View> - <View style={[pal.borderDark, styles.groupContent]}> - <FontAwesomeIcon - icon="lock" - style={[pal.textLight, styles.groupContentIcon]} - /> - <TextInput - testID="newPasswordInput" - style={[pal.text, styles.textInput]} - placeholder="New password" - placeholderTextColor={pal.colors.textLight} - autoCapitalize="none" - autoCorrect={false} - keyboardAppearance={theme.colorScheme} - secureTextEntry - value={password} - onChangeText={setPassword} - editable={!isProcessing} - accessible={true} - accessibilityLabel="Password" - accessibilityHint="Input new password" - /> - </View> - </View> - {error ? ( - <View style={styles.error}> - <View style={styles.errorIcon}> - <FontAwesomeIcon icon="exclamation" style={s.white} size={10} /> - </View> - <View style={s.flex1}> - <Text style={[s.white, s.bold]}>{error}</Text> - </View> - </View> - ) : undefined} - <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> - <TouchableOpacity onPress={onPressBack} accessibilityRole="button"> - <Text type="xl" style={[pal.link, s.pl5]}> - Back - </Text> - </TouchableOpacity> - <View style={s.flex1} /> - {isProcessing ? ( - <ActivityIndicator /> - ) : !resetCode || !password ? ( - <Text type="xl-bold" style={[pal.link, s.pr5, styles.dimmed]}> - Next - </Text> - ) : ( - <TouchableOpacity - testID="setNewPasswordButton" - onPress={onPressNext} - accessibilityRole="button" - accessibilityLabel="Go to next" - accessibilityHint="Navigates to the next screen"> - <Text type="xl-bold" style={[pal.link, s.pr5]}> - Next - </Text> - </TouchableOpacity> - )} - {isProcessing ? ( - <Text type="xl" style={[pal.textLight, s.pl10]}> - Updating... - </Text> - ) : undefined} - </View> - </View> - </> - ) -} - -const PasswordUpdatedForm = ({onPressNext}: {onPressNext: () => void}) => { - const {screen} = useAnalytics() - - useEffect(() => { - screen('Signin:PasswordUpdatedForm') - }, [screen]) - - const pal = usePalette('default') - return ( - <> - <View> - <Text type="title-lg" style={[pal.text, styles.screenTitle]}> - Password updated! - </Text> - <Text type="lg" style={[pal.text, styles.instructions]}> - You can now sign in with your new password. - </Text> - <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> - <View style={s.flex1} /> - <TouchableOpacity - onPress={onPressNext} - accessibilityRole="button" - accessibilityLabel="Close alert" - accessibilityHint="Closes password update alert"> - <Text type="xl-bold" style={[pal.link, s.pr5]}> - Okay - </Text> - </TouchableOpacity> - </View> - </View> - </> - ) -} - -const styles = StyleSheet.create({ - screenTitle: { - marginBottom: 10, - marginHorizontal: 20, - }, - instructions: { - marginBottom: 20, - marginHorizontal: 20, - }, - group: { - borderWidth: 1, - borderRadius: 10, - marginBottom: 20, - marginHorizontal: 20, - }, - groupLabel: { - paddingHorizontal: 20, - paddingBottom: 5, - }, - groupContent: { - borderTopWidth: 1, - flexDirection: 'row', - alignItems: 'center', - }, - noTopBorder: { - borderTopWidth: 0, - }, - groupContentIcon: { - marginLeft: 10, - }, - account: { - borderTopWidth: 1, - paddingHorizontal: 20, - paddingVertical: 4, - }, - accountLast: { - borderBottomWidth: 1, - marginBottom: 20, - paddingVertical: 8, - }, - textInput: { - flex: 1, - width: '100%', - paddingVertical: 10, - paddingHorizontal: 12, - fontSize: 17, - letterSpacing: 0.25, - fontWeight: '400', - borderRadius: 10, - }, - textInputInnerBtn: { - flexDirection: 'row', - alignItems: 'center', - paddingVertical: 6, - paddingHorizontal: 8, - marginHorizontal: 6, - }, - textBtn: { - flexDirection: 'row', - flex: 1, - alignItems: 'center', - }, - textBtnLabel: { - flex: 1, - paddingVertical: 10, - paddingHorizontal: 12, - }, - textBtnFakeInnerBtn: { - flexDirection: 'row', - alignItems: 'center', - borderRadius: 6, - paddingVertical: 6, - paddingHorizontal: 8, - marginHorizontal: 6, - }, - accountText: { - flex: 1, - flexDirection: 'row', - alignItems: 'baseline', - paddingVertical: 10, - }, - accountTextOther: { - paddingLeft: 12, - }, - error: { - backgroundColor: colors.red4, - flexDirection: 'row', - alignItems: 'center', - marginTop: -5, - marginHorizontal: 20, - marginBottom: 15, - borderRadius: 8, - paddingHorizontal: 8, - paddingVertical: 8, - }, - errorIcon: { - borderWidth: 1, - borderColor: colors.white, - color: colors.white, - borderRadius: 30, - width: 16, - height: 16, - alignItems: 'center', - justifyContent: 'center', - marginRight: 5, - }, - dimmed: {opacity: 0.5}, - - maxHeight: { - // @ts-ignore web only -prf - maxHeight: isWeb ? '100vh' : undefined, - height: !isWeb ? '100%' : undefined, - }, -}) diff --git a/src/view/com/auth/login/LoginForm.tsx b/src/view/com/auth/login/LoginForm.tsx new file mode 100644 index 000000000..9779b939a --- /dev/null +++ b/src/view/com/auth/login/LoginForm.tsx @@ -0,0 +1,288 @@ +import React, {useState, useRef} from 'react' +import { + ActivityIndicator, + Keyboard, + TextInput, + TouchableOpacity, + View, +} from 'react-native' +import { + FontAwesomeIcon, + FontAwesomeIconStyle, +} from '@fortawesome/react-native-fontawesome' +import {useAnalytics} from 'lib/analytics/analytics' +import {Text} from '../../util/text/Text' +import {s} from 'lib/styles' +import {createFullHandle} from 'lib/strings/handles' +import {toNiceDomain} from 'lib/strings/url-helpers' +import {RootStoreModel} from 'state/index' +import {ServiceDescription} from 'state/models/session' +import {isNetworkError} from 'lib/strings/errors' +import {usePalette} from 'lib/hooks/usePalette' +import {useTheme} from 'lib/ThemeContext' +import {cleanError} from 'lib/strings/errors' +import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {styles} from './styles' +import {useLingui} from '@lingui/react' +import {useModalControls} from '#/state/modals' + +export const LoginForm = ({ + store, + error, + serviceUrl, + serviceDescription, + initialHandle, + setError, + setServiceUrl, + onPressRetryConnect, + onPressBack, + onPressForgotPassword, +}: { + store: RootStoreModel + error: string + serviceUrl: string + serviceDescription: ServiceDescription | undefined + initialHandle: string + setError: (v: string) => void + setServiceUrl: (v: string) => void + onPressRetryConnect: () => void + onPressBack: () => void + onPressForgotPassword: () => void +}) => { + const {track} = useAnalytics() + const pal = usePalette('default') + const theme = useTheme() + const [isProcessing, setIsProcessing] = useState<boolean>(false) + const [identifier, setIdentifier] = useState<string>(initialHandle) + const [password, setPassword] = useState<string>('') + const passwordInputRef = useRef<TextInput>(null) + const {_} = useLingui() + const {openModal} = useModalControls() + + const onPressSelectService = () => { + openModal({ + name: 'server-input', + initialService: serviceUrl, + onSelect: setServiceUrl, + }) + Keyboard.dismiss() + track('Signin:PressedSelectService') + } + + const onPressNext = async () => { + Keyboard.dismiss() + setError('') + setIsProcessing(true) + + try { + // try to guess the handle if the user just gave their own username + let fullIdent = identifier + if ( + !identifier.includes('@') && // not an email + !identifier.includes('.') && // not a domain + serviceDescription && + serviceDescription.availableUserDomains.length > 0 + ) { + let matched = false + for (const domain of serviceDescription.availableUserDomains) { + if (fullIdent.endsWith(domain)) { + matched = true + } + } + if (!matched) { + fullIdent = createFullHandle( + identifier, + serviceDescription.availableUserDomains[0], + ) + } + } + + await store.session.login({ + service: serviceUrl, + identifier: fullIdent, + password, + }) + } catch (e: any) { + const errMsg = e.toString() + logger.warn('Failed to login', {error: e}) + setIsProcessing(false) + if (errMsg.includes('Authentication Required')) { + setError(_(msg`Invalid username or password`)) + } else if (isNetworkError(e)) { + setError( + _( + msg`Unable to contact your service. Please check your Internet connection.`, + ), + ) + } else { + setError(cleanError(errMsg)) + } + } finally { + track('Sign In', {resumedSession: false}) + } + } + + const isReady = !!serviceDescription && !!identifier && !!password + return ( + <View testID="loginForm"> + <Text type="sm-bold" style={[pal.text, styles.groupLabel]}> + <Trans>Sign into</Trans> + </Text> + <View style={[pal.borderDark, styles.group]}> + <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> + <FontAwesomeIcon + icon="globe" + style={[pal.textLight, styles.groupContentIcon]} + /> + <TouchableOpacity + testID="loginSelectServiceButton" + style={styles.textBtn} + onPress={onPressSelectService} + accessibilityRole="button" + accessibilityLabel={_(msg`Select service`)} + accessibilityHint="Sets server for the Bluesky client"> + <Text type="xl" style={[pal.text, styles.textBtnLabel]}> + {toNiceDomain(serviceUrl)} + </Text> + <View style={[pal.btn, styles.textBtnFakeInnerBtn]}> + <FontAwesomeIcon + icon="pen" + size={12} + style={pal.textLight as FontAwesomeIconStyle} + /> + </View> + </TouchableOpacity> + </View> + </View> + <Text type="sm-bold" style={[pal.text, styles.groupLabel]}> + <Trans>Account</Trans> + </Text> + <View style={[pal.borderDark, styles.group]}> + <View style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> + <FontAwesomeIcon + icon="at" + style={[pal.textLight, styles.groupContentIcon]} + /> + <TextInput + testID="loginUsernameInput" + style={[pal.text, styles.textInput]} + placeholder={_(msg`Username or email address`)} + placeholderTextColor={pal.colors.textLight} + autoCapitalize="none" + autoFocus + autoCorrect={false} + autoComplete="username" + returnKeyType="next" + onSubmitEditing={() => { + passwordInputRef.current?.focus() + }} + blurOnSubmit={false} // prevents flickering due to onSubmitEditing going to next field + keyboardAppearance={theme.colorScheme} + value={identifier} + onChangeText={str => + setIdentifier((str || '').toLowerCase().trim()) + } + editable={!isProcessing} + accessibilityLabel={_(msg`Username or email address`)} + accessibilityHint="Input the username or email address you used at signup" + /> + </View> + <View style={[pal.borderDark, styles.groupContent]}> + <FontAwesomeIcon + icon="lock" + style={[pal.textLight, styles.groupContentIcon]} + /> + <TextInput + testID="loginPasswordInput" + ref={passwordInputRef} + style={[pal.text, styles.textInput]} + placeholder="Password" + placeholderTextColor={pal.colors.textLight} + autoCapitalize="none" + autoCorrect={false} + autoComplete="password" + returnKeyType="done" + enablesReturnKeyAutomatically={true} + keyboardAppearance={theme.colorScheme} + secureTextEntry={true} + textContentType="password" + clearButtonMode="while-editing" + value={password} + onChangeText={setPassword} + onSubmitEditing={onPressNext} + blurOnSubmit={false} // HACK: https://github.com/facebook/react-native/issues/21911#issuecomment-558343069 Keyboard blur behavior is now handled in onSubmitEditing + editable={!isProcessing} + accessibilityLabel={_(msg`Password`)} + accessibilityHint={ + identifier === '' + ? 'Input your password' + : `Input the password tied to ${identifier}` + } + /> + <TouchableOpacity + testID="forgotPasswordButton" + style={styles.textInputInnerBtn} + onPress={onPressForgotPassword} + accessibilityRole="button" + accessibilityLabel={_(msg`Forgot password`)} + accessibilityHint="Opens password reset form"> + <Text style={pal.link}> + <Trans>Forgot</Trans> + </Text> + </TouchableOpacity> + </View> + </View> + {error ? ( + <View style={styles.error}> + <View style={styles.errorIcon}> + <FontAwesomeIcon icon="exclamation" style={s.white} size={10} /> + </View> + <View style={s.flex1}> + <Text style={[s.white, s.bold]}>{error}</Text> + </View> + </View> + ) : undefined} + <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> + <TouchableOpacity onPress={onPressBack} accessibilityRole="button"> + <Text type="xl" style={[pal.link, s.pl5]}> + <Trans>Back</Trans> + </Text> + </TouchableOpacity> + <View style={s.flex1} /> + {!serviceDescription && error ? ( + <TouchableOpacity + testID="loginRetryButton" + onPress={onPressRetryConnect} + accessibilityRole="button" + accessibilityLabel={_(msg`Retry`)} + accessibilityHint="Retries login"> + <Text type="xl-bold" style={[pal.link, s.pr5]}> + <Trans>Retry</Trans> + </Text> + </TouchableOpacity> + ) : !serviceDescription ? ( + <> + <ActivityIndicator /> + <Text type="xl" style={[pal.textLight, s.pl10]}> + <Trans>Connecting...</Trans> + </Text> + </> + ) : isProcessing ? ( + <ActivityIndicator /> + ) : isReady ? ( + <TouchableOpacity + testID="loginNextButton" + onPress={onPressNext} + accessibilityRole="button" + accessibilityLabel={_(msg`Go to next`)} + accessibilityHint="Navigates to the next screen"> + <Text type="xl-bold" style={[pal.link, s.pr5]}> + <Trans>Next</Trans> + </Text> + </TouchableOpacity> + ) : undefined} + </View> + </View> + ) +} diff --git a/src/view/com/auth/login/PasswordUpdatedForm.tsx b/src/view/com/auth/login/PasswordUpdatedForm.tsx new file mode 100644 index 000000000..1e07588a9 --- /dev/null +++ b/src/view/com/auth/login/PasswordUpdatedForm.tsx @@ -0,0 +1,48 @@ +import React, {useEffect} from 'react' +import {TouchableOpacity, View} from 'react-native' +import {useAnalytics} from 'lib/analytics/analytics' +import {Text} from '../../util/text/Text' +import {s} from 'lib/styles' +import {usePalette} from 'lib/hooks/usePalette' +import {styles} from './styles' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +export const PasswordUpdatedForm = ({ + onPressNext, +}: { + onPressNext: () => void +}) => { + const {screen} = useAnalytics() + const pal = usePalette('default') + const {_} = useLingui() + + useEffect(() => { + screen('Signin:PasswordUpdatedForm') + }, [screen]) + + return ( + <> + <View> + <Text type="title-lg" style={[pal.text, styles.screenTitle]}> + <Trans>Password updated!</Trans> + </Text> + <Text type="lg" style={[pal.text, styles.instructions]}> + <Trans>You can now sign in with your new password.</Trans> + </Text> + <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> + <View style={s.flex1} /> + <TouchableOpacity + onPress={onPressNext} + accessibilityRole="button" + accessibilityLabel={_(msg`Close alert`)} + accessibilityHint="Closes password update alert"> + <Text type="xl-bold" style={[pal.link, s.pr5]}> + <Trans>Okay</Trans> + </Text> + </TouchableOpacity> + </View> + </View> + </> + ) +} diff --git a/src/view/com/auth/login/SetNewPasswordForm.tsx b/src/view/com/auth/login/SetNewPasswordForm.tsx new file mode 100644 index 000000000..04eaa2842 --- /dev/null +++ b/src/view/com/auth/login/SetNewPasswordForm.tsx @@ -0,0 +1,181 @@ +import React, {useState, useEffect} from 'react' +import { + ActivityIndicator, + TextInput, + TouchableOpacity, + View, +} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {BskyAgent} from '@atproto/api' +import {useAnalytics} from 'lib/analytics/analytics' +import {Text} from '../../util/text/Text' +import {s} from 'lib/styles' +import {RootStoreModel} from 'state/index' +import {isNetworkError} from 'lib/strings/errors' +import {usePalette} from 'lib/hooks/usePalette' +import {useTheme} from 'lib/ThemeContext' +import {cleanError} from 'lib/strings/errors' +import {logger} from '#/logger' +import {styles} from './styles' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +export const SetNewPasswordForm = ({ + error, + serviceUrl, + setError, + onPressBack, + onPasswordSet, +}: { + store: RootStoreModel + error: string + serviceUrl: string + setError: (v: string) => void + onPressBack: () => void + onPasswordSet: () => void +}) => { + const pal = usePalette('default') + const theme = useTheme() + const {screen} = useAnalytics() + const {_} = useLingui() + + useEffect(() => { + screen('Signin:SetNewPasswordForm') + }, [screen]) + + const [isProcessing, setIsProcessing] = useState<boolean>(false) + const [resetCode, setResetCode] = useState<string>('') + const [password, setPassword] = useState<string>('') + + const onPressNext = async () => { + setError('') + setIsProcessing(true) + + try { + const agent = new BskyAgent({service: serviceUrl}) + const token = resetCode.replace(/\s/g, '') + await agent.com.atproto.server.resetPassword({ + token, + password, + }) + onPasswordSet() + } catch (e: any) { + const errMsg = e.toString() + logger.warn('Failed to set new password', {error: e}) + setIsProcessing(false) + if (isNetworkError(e)) { + setError( + 'Unable to contact your service. Please check your Internet connection.', + ) + } else { + setError(cleanError(errMsg)) + } + } + } + + return ( + <> + <View> + <Text type="title-lg" style={[pal.text, styles.screenTitle]}> + <Trans>Set new password</Trans> + </Text> + <Text type="lg" style={[pal.text, styles.instructions]}> + <Trans> + You will receive an email with a "reset code." Enter that code here, + then enter your new password. + </Trans> + </Text> + <View + testID="newPasswordView" + style={[pal.view, pal.borderDark, styles.group]}> + <View + style={[pal.borderDark, styles.groupContent, styles.noTopBorder]}> + <FontAwesomeIcon + icon="ticket" + style={[pal.textLight, styles.groupContentIcon]} + /> + <TextInput + testID="resetCodeInput" + style={[pal.text, styles.textInput]} + placeholder="Reset code" + placeholderTextColor={pal.colors.textLight} + autoCapitalize="none" + autoCorrect={false} + keyboardAppearance={theme.colorScheme} + autoFocus + value={resetCode} + onChangeText={setResetCode} + editable={!isProcessing} + accessible={true} + accessibilityLabel={_(msg`Reset code`)} + accessibilityHint="Input code sent to your email for password reset" + /> + </View> + <View style={[pal.borderDark, styles.groupContent]}> + <FontAwesomeIcon + icon="lock" + style={[pal.textLight, styles.groupContentIcon]} + /> + <TextInput + testID="newPasswordInput" + style={[pal.text, styles.textInput]} + placeholder="New password" + placeholderTextColor={pal.colors.textLight} + autoCapitalize="none" + autoCorrect={false} + keyboardAppearance={theme.colorScheme} + secureTextEntry + value={password} + onChangeText={setPassword} + editable={!isProcessing} + accessible={true} + accessibilityLabel={_(msg`Password`)} + accessibilityHint="Input new password" + /> + </View> + </View> + {error ? ( + <View style={styles.error}> + <View style={styles.errorIcon}> + <FontAwesomeIcon icon="exclamation" style={s.white} size={10} /> + </View> + <View style={s.flex1}> + <Text style={[s.white, s.bold]}>{error}</Text> + </View> + </View> + ) : undefined} + <View style={[s.flexRow, s.alignCenter, s.pl20, s.pr20]}> + <TouchableOpacity onPress={onPressBack} accessibilityRole="button"> + <Text type="xl" style={[pal.link, s.pl5]}> + <Trans>Back</Trans> + </Text> + </TouchableOpacity> + <View style={s.flex1} /> + {isProcessing ? ( + <ActivityIndicator /> + ) : !resetCode || !password ? ( + <Text type="xl-bold" style={[pal.link, s.pr5, styles.dimmed]}> + <Trans>Next</Trans> + </Text> + ) : ( + <TouchableOpacity + testID="setNewPasswordButton" + onPress={onPressNext} + accessibilityRole="button" + accessibilityLabel={_(msg`Go to next`)} + accessibilityHint="Navigates to the next screen"> + <Text type="xl-bold" style={[pal.link, s.pr5]}> + <Trans>Next</Trans> + </Text> + </TouchableOpacity> + )} + {isProcessing ? ( + <Text type="xl" style={[pal.textLight, s.pl10]}> + <Trans>Updating...</Trans> + </Text> + ) : undefined} + </View> + </View> + </> + ) +} diff --git a/src/view/com/auth/login/styles.ts b/src/view/com/auth/login/styles.ts new file mode 100644 index 000000000..9dccc2803 --- /dev/null +++ b/src/view/com/auth/login/styles.ts @@ -0,0 +1,118 @@ +import {StyleSheet} from 'react-native' +import {colors} from 'lib/styles' +import {isWeb} from '#/platform/detection' + +export const styles = StyleSheet.create({ + screenTitle: { + marginBottom: 10, + marginHorizontal: 20, + }, + instructions: { + marginBottom: 20, + marginHorizontal: 20, + }, + group: { + borderWidth: 1, + borderRadius: 10, + marginBottom: 20, + marginHorizontal: 20, + }, + groupLabel: { + paddingHorizontal: 20, + paddingBottom: 5, + }, + groupContent: { + borderTopWidth: 1, + flexDirection: 'row', + alignItems: 'center', + }, + noTopBorder: { + borderTopWidth: 0, + }, + groupContentIcon: { + marginLeft: 10, + }, + account: { + borderTopWidth: 1, + paddingHorizontal: 20, + paddingVertical: 4, + }, + accountLast: { + borderBottomWidth: 1, + marginBottom: 20, + paddingVertical: 8, + }, + textInput: { + flex: 1, + width: '100%', + paddingVertical: 10, + paddingHorizontal: 12, + fontSize: 17, + letterSpacing: 0.25, + fontWeight: '400', + borderRadius: 10, + }, + textInputInnerBtn: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 6, + paddingHorizontal: 8, + marginHorizontal: 6, + }, + textBtn: { + flexDirection: 'row', + flex: 1, + alignItems: 'center', + }, + textBtnLabel: { + flex: 1, + paddingVertical: 10, + paddingHorizontal: 12, + }, + textBtnFakeInnerBtn: { + flexDirection: 'row', + alignItems: 'center', + borderRadius: 6, + paddingVertical: 6, + paddingHorizontal: 8, + marginHorizontal: 6, + }, + accountText: { + flex: 1, + flexDirection: 'row', + alignItems: 'baseline', + paddingVertical: 10, + }, + accountTextOther: { + paddingLeft: 12, + }, + error: { + backgroundColor: colors.red4, + flexDirection: 'row', + alignItems: 'center', + marginTop: -5, + marginHorizontal: 20, + marginBottom: 15, + borderRadius: 8, + paddingHorizontal: 8, + paddingVertical: 8, + }, + errorIcon: { + borderWidth: 1, + borderColor: colors.white, + color: colors.white, + borderRadius: 30, + width: 16, + height: 16, + alignItems: 'center', + justifyContent: 'center', + marginRight: 5, + }, + dimmed: {opacity: 0.5}, + + maxHeight: { + // @ts-ignore web only -prf + maxHeight: isWeb ? '100vh' : undefined, + height: !isWeb ? '100%' : undefined, + }, +}) diff --git a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx index f672372b8..7ec78bd7f 100644 --- a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx +++ b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx @@ -14,6 +14,7 @@ import {Text} from 'view/com/util/text/Text' import Animated, {FadeInRight} from 'react-native-reanimated' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useAnalytics} from 'lib/analytics/analytics' +import {Trans} from '@lingui/macro' type Props = { item: SuggestedActor @@ -115,7 +116,9 @@ export const ProfileCard = observer(function ProfileCardImpl({ {addingMoreSuggestions ? ( <View style={styles.addingMoreContainer}> <ActivityIndicator size="small" color={pal.colors.text} /> - <Text style={[pal.text]}>Finding similar accounts...</Text> + <Text style={[pal.text]}> + <Trans>Finding similar accounts...</Trans> + </Text> </View> ) : null} </View> diff --git a/src/view/com/auth/onboarding/WelcomeMobile.tsx b/src/view/com/auth/onboarding/WelcomeMobile.tsx index 1f0a64370..ef70a1fe3 100644 --- a/src/view/com/auth/onboarding/WelcomeMobile.tsx +++ b/src/view/com/auth/onboarding/WelcomeMobile.tsx @@ -7,6 +7,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Button} from 'view/com/util/forms/Button' import {observer} from 'mobx-react-lite' import {ViewHeader} from 'view/com/util/ViewHeader' +import {Trans} from '@lingui/macro' type Props = { next: () => void @@ -32,7 +33,9 @@ export const WelcomeMobile = observer(function WelcomeMobileImpl({ accessibilityRole="button" style={[s.flexRow, s.alignCenter]} onPress={skip}> - <Text style={[pal.link]}>Skip</Text> + <Text style={[pal.link]}> + <Trans>Skip</Trans> + </Text> <FontAwesomeIcon icon={'chevron-right'} size={14} @@ -45,17 +48,21 @@ export const WelcomeMobile = observer(function WelcomeMobileImpl({ <View> <Text style={[pal.text, styles.title]}> Welcome to{' '} - <Text style={[pal.text, pal.link, styles.title]}>Bluesky</Text> + <Text style={[pal.text, pal.link, styles.title]}> + <Trans>Bluesky</Trans> + </Text> </Text> <View style={styles.spacer} /> <View style={[styles.row]}> <FontAwesomeIcon icon={'globe'} size={36} color={pal.colors.link} /> <View style={[styles.rowText]}> <Text type="lg-bold" style={[pal.text]}> - Bluesky is public. + <Trans>Bluesky is public.</Trans> </Text> <Text type="lg-thin" style={[pal.text, s.pt2]}> - Your posts, likes, and blocks are public. Mutes are private. + <Trans> + Your posts, likes, and blocks are public. Mutes are private. + </Trans> </Text> </View> </View> @@ -63,10 +70,10 @@ export const WelcomeMobile = observer(function WelcomeMobileImpl({ <FontAwesomeIcon icon={'at'} size={36} color={pal.colors.link} /> <View style={[styles.rowText]}> <Text type="lg-bold" style={[pal.text]}> - Bluesky is open. + <Trans>Bluesky is open.</Trans> </Text> <Text type="lg-thin" style={[pal.text, s.pt2]}> - Never lose access to your followers and data. + <Trans>Never lose access to your followers and data.</Trans> </Text> </View> </View> @@ -74,11 +81,13 @@ export const WelcomeMobile = observer(function WelcomeMobileImpl({ <FontAwesomeIcon icon={'gear'} size={36} color={pal.colors.link} /> <View style={[styles.rowText]}> <Text type="lg-bold" style={[pal.text]}> - Bluesky is flexible. + <Trans>Bluesky is flexible.</Trans> </Text> <Text type="lg-thin" style={[pal.text, s.pt2]}> - Choose the algorithms that power your experience with custom - feeds. + <Trans> + Choose the algorithms that power your experience with custom + feeds. + </Trans> </Text> </View> </View> diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index cdc9768f7..8f8c2eea3 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -49,6 +49,8 @@ import {LabelsBtn} from './labels/LabelsBtn' import {SelectLangBtn} from './select-language/SelectLangBtn' import {EmojiPickerButton} from './text-input/web/EmojiPicker.web' import {insertMentionAt} from 'lib/strings/mention-manip' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModals, useModalControls} from '#/state/modals' import {useRequireAltTextEnabled} from '#/state/preferences' import { @@ -70,6 +72,7 @@ export const ComposePost = observer(function ComposePost({ const pal = usePalette('default') const {isDesktop, isMobile} = useWebMediaQueries() const store = useStores() + const {_} = useLingui() const requireAltTextEnabled = useRequireAltTextEnabled() const langPrefs = useLanguagePrefs() const setLangPrefs = useLanguagePrefsApi() @@ -273,9 +276,11 @@ export const ComposePost = observer(function ComposePost({ onPress={onPressCancel} onAccessibilityEscape={onPressCancel} accessibilityRole="button" - accessibilityLabel="Cancel" + accessibilityLabel={_(msg`Cancel`)} accessibilityHint="Closes post composer and discards post draft"> - <Text style={[pal.link, s.f18]}>Cancel</Text> + <Text style={[pal.link, s.f18]}> + <Trans>Cancel</Trans> + </Text> </TouchableOpacity> <View style={s.flex1} /> {isProcessing ? ( @@ -316,7 +321,9 @@ export const ComposePost = observer(function ComposePost({ </TouchableOpacity> ) : ( <View style={[styles.postBtn, pal.btn]}> - <Text style={[pal.textLight, s.f16, s.bold]}>Post</Text> + <Text style={[pal.textLight, s.f16, s.bold]}> + <Trans>Post</Trans> + </Text> </View> )} </> @@ -332,7 +339,7 @@ export const ComposePost = observer(function ComposePost({ /> </View> <Text style={[pal.text, s.flex1]}> - One or more images is missing alt text. + <Trans>One or more images is missing alt text.</Trans> </Text> </View> )} @@ -388,7 +395,7 @@ export const ComposePost = observer(function ComposePost({ onSuggestedLinksChanged={setSuggestedLinks} onError={setError} accessible={true} - accessibilityLabel="Write post" + accessibilityLabel={_(msg`Write post`)} accessibilityHint={`Compose posts up to ${MAX_GRAPHEME_LENGTH} characters in length`} /> </View> @@ -417,11 +424,11 @@ export const ComposePost = observer(function ComposePost({ style={[pal.borderDark, styles.addExtLinkBtn]} onPress={() => onPressAddLinkCard(url)} accessibilityRole="button" - accessibilityLabel="Add link card" + accessibilityLabel={_(msg`Add link card`)} accessibilityHint={`Creates a card with a thumbnail. The card links to ${url}`}> <Text style={pal.text}> - Add link card:{' '} - <Text style={pal.link}>{toShortUrl(url)}</Text> + <Trans>Add link card:</Trans> + <Text style={[pal.link, s.ml5]}>{toShortUrl(url)}</Text> </Text> </TouchableOpacity> ))} diff --git a/src/view/com/composer/ExternalEmbed.tsx b/src/view/com/composer/ExternalEmbed.tsx index c9200ec63..502e4b4d2 100644 --- a/src/view/com/composer/ExternalEmbed.tsx +++ b/src/view/com/composer/ExternalEmbed.tsx @@ -11,6 +11,8 @@ import {Text} from '../util/text/Text' import {s} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {ExternalEmbedDraft} from 'lib/api/index' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' export const ExternalEmbed = ({ link, @@ -21,6 +23,7 @@ export const ExternalEmbed = ({ }) => { const pal = usePalette('default') const palError = usePalette('error') + const {_} = useLingui() if (!link) { return <View /> } @@ -64,7 +67,7 @@ export const ExternalEmbed = ({ style={styles.removeBtn} onPress={onRemove} accessibilityRole="button" - accessibilityLabel="Remove image preview" + accessibilityLabel={_(msg`Remove image preview`)} accessibilityHint={`Removes default thumbnail from ${link.uri}`} onAccessibilityEscape={onRemove}> <FontAwesomeIcon size={18} icon="xmark" style={s.white} /> diff --git a/src/view/com/composer/Prompt.tsx b/src/view/com/composer/Prompt.tsx index e54404f52..5e8a01d96 100644 --- a/src/view/com/composer/Prompt.tsx +++ b/src/view/com/composer/Prompt.tsx @@ -5,10 +5,13 @@ import {Text} from '../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) { const store = useStores() const pal = usePalette('default') + const {_} = useLingui() const {isDesktop} = useWebMediaQueries() return ( <TouchableOpacity @@ -16,7 +19,7 @@ export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) { style={[pal.view, pal.border, styles.prompt]} onPress={() => onPressCompose()} accessibilityRole="button" - accessibilityLabel="Compose reply" + accessibilityLabel={_(msg`Compose reply`)} accessibilityHint="Opens composer"> <UserAvatar avatar={store.me.avatar} size={38} /> <Text @@ -25,7 +28,7 @@ export function ComposePrompt({onPressCompose}: {onPressCompose: () => void}) { pal.text, isDesktop ? styles.labelDesktopWeb : styles.labelMobile, ]}> - Write your reply + <Trans>Write your reply</Trans> </Text> </TouchableOpacity> ) diff --git a/src/view/com/composer/labels/LabelsBtn.tsx b/src/view/com/composer/labels/LabelsBtn.tsx index 4b6ad81c7..7d90774f2 100644 --- a/src/view/com/composer/labels/LabelsBtn.tsx +++ b/src/view/com/composer/labels/LabelsBtn.tsx @@ -7,6 +7,8 @@ import {ShieldExclamation} from 'lib/icons' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' import {isNative} from 'platform/detection' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useModalControls} from '#/state/modals' export const LabelsBtn = observer(function LabelsBtn({ @@ -19,6 +21,7 @@ export const LabelsBtn = observer(function LabelsBtn({ onChange: (v: string[]) => void }) { const pal = usePalette('default') + const {_} = useLingui() const {openModal} = useModalControls() return ( @@ -26,7 +29,7 @@ export const LabelsBtn = observer(function LabelsBtn({ type="default-light" testID="labelsBtn" style={[styles.button, !hasMedia && styles.dimmed]} - accessibilityLabel="Content warnings" + accessibilityLabel={_(msg`Content warnings`)} accessibilityHint="" onPress={() => { if (isNative) { diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index 069a05475..69c8debb0 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -10,6 +10,8 @@ import {Text} from 'view/com/util/text/Text' import {Dimensions} from 'lib/media/types' import {usePalette} from 'lib/hooks/usePalette' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' import {isNative} from 'platform/detection' @@ -48,6 +50,7 @@ const GalleryInner = observer(function GalleryImpl({ containerInfo, }: GalleryInnerProps) { const pal = usePalette('default') + const {_} = useLingui() const {isMobile} = useWebMediaQueries() const {openModal} = useModalControls() @@ -113,7 +116,7 @@ const GalleryInner = observer(function GalleryImpl({ <TouchableOpacity testID="altTextButton" accessibilityRole="button" - accessibilityLabel="Add alt text" + accessibilityLabel={_(msg`Add alt text`)} accessibilityHint="" onPress={() => { Keyboard.dismiss() @@ -124,7 +127,7 @@ const GalleryInner = observer(function GalleryImpl({ }} style={[styles.altTextControl, altTextControlStyle]}> <Text style={styles.altTextControlLabel} accessible={false}> - ALT + <Trans>ALT</Trans> </Text> {image.altText.length > 0 ? ( <FontAwesomeIcon @@ -138,7 +141,7 @@ const GalleryInner = observer(function GalleryImpl({ <TouchableOpacity testID="editPhotoButton" accessibilityRole="button" - accessibilityLabel="Edit image" + accessibilityLabel={_(msg`Edit image`)} accessibilityHint="" onPress={() => { if (isNative) { @@ -161,7 +164,7 @@ const GalleryInner = observer(function GalleryImpl({ <TouchableOpacity testID="removePhotoButton" accessibilityRole="button" - accessibilityLabel="Remove image" + accessibilityLabel={_(msg`Remove image`)} accessibilityHint="" onPress={() => gallery.remove(image)} style={styles.imageControl}> @@ -174,7 +177,7 @@ const GalleryInner = observer(function GalleryImpl({ </View> <TouchableOpacity accessibilityRole="button" - accessibilityLabel="Add alt text" + accessibilityLabel={_(msg`Add alt text`)} accessibilityHint="" onPress={() => { Keyboard.dismiss() @@ -203,8 +206,10 @@ const GalleryInner = observer(function GalleryImpl({ <FontAwesomeIcon icon="info" size={12} color={pal.colors.text} /> </View> <Text type="sm" style={[pal.textLight, s.flex1]}> - Alt text describes images for blind and low-vision users, and helps - give context to everyone. + <Trans> + Alt text describes images for blind and low-vision users, and helps + give context to everyone. + </Trans> </Text> </View> </> diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx index 99e820d51..4eac7179e 100644 --- a/src/view/com/composer/photos/OpenCameraBtn.tsx +++ b/src/view/com/composer/photos/OpenCameraBtn.tsx @@ -13,6 +13,8 @@ import {HITSLOP_10, POST_IMG_MAX} from 'lib/constants' import {GalleryModel} from 'state/models/media/gallery' import {isMobileWeb, isNative} from 'platform/detection' import {logger} from '#/logger' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' type Props = { gallery: GalleryModel @@ -22,6 +24,7 @@ export function OpenCameraBtn({gallery}: Props) { const pal = usePalette('default') const {track} = useAnalytics() const store = useStores() + const {_} = useLingui() const {requestCameraAccessIfNeeded} = useCameraPermission() const onPressTakePicture = useCallback(async () => { @@ -56,7 +59,7 @@ export function OpenCameraBtn({gallery}: Props) { style={styles.button} hitSlop={HITSLOP_10} accessibilityRole="button" - accessibilityLabel="Camera" + accessibilityLabel={_(msg`Camera`)} accessibilityHint="Opens camera on device"> <FontAwesomeIcon icon="camera" diff --git a/src/view/com/composer/photos/SelectPhotoBtn.tsx b/src/view/com/composer/photos/SelectPhotoBtn.tsx index a6826eb98..af0a22b01 100644 --- a/src/view/com/composer/photos/SelectPhotoBtn.tsx +++ b/src/view/com/composer/photos/SelectPhotoBtn.tsx @@ -10,6 +10,8 @@ import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions' import {GalleryModel} from 'state/models/media/gallery' import {HITSLOP_10} from 'lib/constants' import {isNative} from 'platform/detection' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' type Props = { gallery: GalleryModel @@ -18,6 +20,7 @@ type Props = { export function SelectPhotoBtn({gallery}: Props) { const pal = usePalette('default') const {track} = useAnalytics() + const {_} = useLingui() const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() const onPressSelectPhotos = useCallback(async () => { @@ -37,7 +40,7 @@ export function SelectPhotoBtn({gallery}: Props) { style={styles.button} hitSlop={HITSLOP_10} accessibilityRole="button" - accessibilityLabel="Gallery" + accessibilityLabel={_(msg`Gallery`)} accessibilityHint="Opens device photo gallery"> <FontAwesomeIcon icon={['far', 'image']} diff --git a/src/view/com/composer/select-language/SelectLangBtn.tsx b/src/view/com/composer/select-language/SelectLangBtn.tsx index 896df041e..7ea63ed62 100644 --- a/src/view/com/composer/select-language/SelectLangBtn.tsx +++ b/src/view/com/composer/select-language/SelectLangBtn.tsx @@ -21,9 +21,12 @@ import { toPostLanguages, hasPostLanguage, } from '#/state/preferences/languages' +import {t, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export const SelectLangBtn = observer(function SelectLangBtn() { const pal = usePalette('default') + const {_} = useLingui() const {openModal} = useModalControls() const langPrefs = useLanguagePrefs() const setLangPrefs = useLanguagePrefsApi() @@ -82,11 +85,11 @@ export const SelectLangBtn = observer(function SelectLangBtn() { } return [ - {heading: true, label: 'Post language'}, + {heading: true, label: t`Post language`}, ...arr.slice(0, 6), {sep: true}, { - label: 'Other...', + label: t`Other...`, onPress: onPressMore, }, ] @@ -99,7 +102,7 @@ export const SelectLangBtn = observer(function SelectLangBtn() { items={items} openUpwards style={styles.button} - accessibilityLabel="Language selection" + accessibilityLabel={_(msg`Language selection`)} accessibilityHint=""> {postLanguagesPref.length > 0 ? ( <Text type="lg-bold" style={[pal.link, styles.label]} numberOfLines={1}> diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index 1037007b7..044f69efe 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -21,6 +21,8 @@ import {FAB} from '../util/fab/FAB' import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn' import useAppState from 'react-native-appstate-hook' import {logger} from '#/logger' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export const FeedPage = observer(function FeedPageImpl({ testID, @@ -37,6 +39,7 @@ export const FeedPage = observer(function FeedPageImpl({ }) { const store = useStores() const pal = usePalette('default') + const {_} = useLingui() const {isDesktop} = useWebMediaQueries() const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll() const {screen, track} = useAnalytics() @@ -157,7 +160,7 @@ export const FeedPage = observer(function FeedPageImpl({ type="title-lg" href="/settings/home-feed" style={{fontWeight: 'bold'}} - accessibilityLabel="Feed Preferences" + accessibilityLabel={_(msg`Feed Preferences`)} accessibilityHint="" text={ <FontAwesomeIcon @@ -170,7 +173,7 @@ export const FeedPage = observer(function FeedPageImpl({ ) } return <></> - }, [isDesktop, pal, store, hasNew]) + }, [isDesktop, pal.view, pal.text, pal.textLight, store, hasNew, _]) return ( <View testID={testID} style={s.h100pct}> @@ -188,7 +191,7 @@ export const FeedPage = observer(function FeedPageImpl({ {(isScrolledDown || hasNew) && ( <LoadLatestBtn onPress={onPressLoadLatest} - label="Load new posts" + label={_(msg`Load new posts`)} showIndicator={hasNew} /> )} @@ -197,7 +200,7 @@ export const FeedPage = observer(function FeedPageImpl({ onPress={onPressCompose} icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />} accessibilityRole="button" - accessibilityLabel="New post" + accessibilityLabel={_(msg`New post`)} accessibilityHint="" /> </View> diff --git a/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx b/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx index bb006d506..c806bc6a6 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx @@ -5,10 +5,10 @@ * LICENSE file in the root directory of this source tree. * */ - -import {createHitslop} from 'lib/constants' import React from 'react' +import {createHitslop} from 'lib/constants' import {SafeAreaView, Text, TouchableOpacity, StyleSheet} from 'react-native' +import {t} from '@lingui/macro' type Props = { onRequestClose: () => void @@ -23,7 +23,7 @@ const ImageDefaultHeader = ({onRequestClose}: Props) => ( onPress={onRequestClose} hitSlop={HIT_SLOP} accessibilityRole="button" - accessibilityLabel="Close image" + accessibilityLabel={t`Close image`} accessibilityHint="Closes viewer for header image" onAccessibilityEscape={onRequestClose}> <Text style={styles.closeText}>✕</Text> diff --git a/src/view/com/lightbox/Lightbox.web.tsx b/src/view/com/lightbox/Lightbox.web.tsx index ddf965f42..331a2b823 100644 --- a/src/view/com/lightbox/Lightbox.web.tsx +++ b/src/view/com/lightbox/Lightbox.web.tsx @@ -14,6 +14,8 @@ import * as models from 'state/models/ui/shell' import {colors, s} from 'lib/styles' import ImageDefaultHeader from './ImageViewing/components/ImageDefaultHeader' import {Text} from '../util/text/Text' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' interface Img { uri: string @@ -62,6 +64,7 @@ function LightboxInner({ initialIndex: number onClose: () => void }) { + const {_} = useLingui() const [index, setIndex] = useState<number>(initialIndex) const [isAltExpanded, setAltExpanded] = useState(false) @@ -101,7 +104,7 @@ function LightboxInner({ <TouchableWithoutFeedback onPress={onClose} accessibilityRole="button" - accessibilityLabel="Close image viewer" + accessibilityLabel={_(msg`Close image viewer`)} accessibilityHint="Exits image view" onAccessibilityEscape={onClose}> <View style={styles.imageCenterer}> @@ -117,7 +120,7 @@ function LightboxInner({ onPress={onPressLeft} style={[styles.btn, styles.leftBtn]} accessibilityRole="button" - accessibilityLabel="Previous image" + accessibilityLabel={_(msg`Previous image`)} accessibilityHint=""> <FontAwesomeIcon icon="angle-left" @@ -131,7 +134,7 @@ function LightboxInner({ onPress={onPressRight} style={[styles.btn, styles.rightBtn]} accessibilityRole="button" - accessibilityLabel="Next image" + accessibilityLabel={_(msg`Next image`)} accessibilityHint=""> <FontAwesomeIcon icon="angle-right" @@ -145,7 +148,7 @@ function LightboxInner({ {imgs[index].alt ? ( <View style={styles.footer}> <Pressable - accessibilityLabel="Expand alt text" + accessibilityLabel={_(msg`Expand alt text`)} accessibilityHint="If alt text is long, toggles alt text expanded state" onPress={() => { setAltExpanded(!isAltExpanded) diff --git a/src/view/com/lists/ListsList.tsx b/src/view/com/lists/ListsList.tsx index 8c6510886..2883a31d5 100644 --- a/src/view/com/lists/ListsList.tsx +++ b/src/view/com/lists/ListsList.tsx @@ -20,6 +20,7 @@ import {usePalette} from 'lib/hooks/usePalette' import {FlatList} from '../util/Views' import {s} from 'lib/styles' import {logger} from '#/logger' +import {Trans} from '@lingui/macro' const LOADING = {_reactKey: '__loading__'} const EMPTY = {_reactKey: '__empty__'} @@ -107,7 +108,9 @@ export const ListsList = observer(function ListsListImpl({ <View testID="listsEmpty" style={[{padding: 18, borderTopWidth: 1}, pal.border]}> - <Text style={pal.textLight}>You have no lists.</Text> + <Text style={pal.textLight}> + <Trans>You have no lists.</Trans> + </Text> </View> ) } else if (item === ERROR_ITEM) { diff --git a/src/view/com/modals/AddAppPasswords.tsx b/src/view/com/modals/AddAppPasswords.tsx index 621c61b90..095ad48bd 100644 --- a/src/view/com/modals/AddAppPasswords.tsx +++ b/src/view/com/modals/AddAppPasswords.tsx @@ -13,6 +13,8 @@ import { import Clipboard from '@react-native-clipboard/clipboard' import * as Toast from '../util/Toast' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['70%'] @@ -55,6 +57,7 @@ const shadesOfBlue: string[] = [ export function Component({}: {}) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const {closeModal} = useModalControls() const [name, setName] = useState( shadesOfBlue[Math.floor(Math.random() * shadesOfBlue.length)], @@ -121,15 +124,19 @@ export function Component({}: {}) { <View> {!appPassword ? ( <Text type="lg" style={[pal.text]}> - Please enter a unique name for this App Password or use our randomly - generated one. + <Trans> + Please enter a unique name for this App Password or use our + randomly generated one. + </Trans> </Text> ) : ( <Text type="lg" style={[pal.text]}> - <Text type="lg-bold" style={[pal.text]}> - Here is your app password. - </Text>{' '} - Use this to sign into the other app along with your handle. + <Text type="lg-bold" style={[pal.text, s.mr5]}> + <Trans>Here is your app password.</Trans> + </Text> + <Trans> + Use this to sign into the other app along with your handle. + </Trans> </Text> )} {!appPassword ? ( @@ -154,7 +161,7 @@ export function Component({}: {}) { returnKeyType="done" onEndEditing={createAppPassword} accessible={true} - accessibilityLabel="Name" + accessibilityLabel={_(msg`Name`)} accessibilityHint="Input name for app password" /> </View> @@ -163,13 +170,15 @@ export function Component({}: {}) { style={[pal.border, styles.passwordContainer, pal.btn]} onPress={onCopy} accessibilityRole="button" - accessibilityLabel="Copy" + accessibilityLabel={_(msg`Copy`)} accessibilityHint="Copies app password"> <Text type="2xl-bold" style={[pal.text]}> {appPassword} </Text> {wasCopied ? ( - <Text style={[pal.textLight]}>Copied</Text> + <Text style={[pal.textLight]}> + <Trans>Copied</Trans> + </Text> ) : ( <FontAwesomeIcon icon={['far', 'clone']} @@ -182,14 +191,18 @@ export function Component({}: {}) { </View> {appPassword ? ( <Text type="lg" style={[pal.textLight, s.mb10]}> - For security reasons, you won't be able to view this again. If you - lose this password, you'll need to generate a new one. + <Trans> + For security reasons, you won't be able to view this again. If you + lose this password, you'll need to generate a new one. + </Trans> </Text> ) : ( <Text type="xs" style={[pal.textLight, s.mb10, s.mt2]}> - Can only contain letters, numbers, spaces, dashes, and underscores. - Must be at least 4 characters long, but no more than 32 characters - long. + <Trans> + Can only contain letters, numbers, spaces, dashes, and underscores. + Must be at least 4 characters long, but no more than 32 characters + long. + </Trans> </Text> )} <View style={styles.btnContainer}> diff --git a/src/view/com/modals/AltImage.tsx b/src/view/com/modals/AltImage.tsx index 9c377a121..80130f43a 100644 --- a/src/view/com/modals/AltImage.tsx +++ b/src/view/com/modals/AltImage.tsx @@ -19,6 +19,8 @@ import {Text} from '../util/text/Text' import LinearGradient from 'react-native-linear-gradient' import {isAndroid, isWeb} from 'platform/detection' import {ImageModel} from 'state/models/media/image' +import {useLingui} from '@lingui/react' +import {Trans, msg} from '@lingui/macro' import {useModalControls} from '#/state/modals' export const snapPoints = ['fullscreen'] @@ -30,6 +32,7 @@ interface Props { export function Component({image}: Props) { const pal = usePalette('default') const theme = useTheme() + const {_} = useLingui() const [altText, setAltText] = useState(image.altText) const windim = useWindowDimensions() const {closeModal} = useModalControls() @@ -90,7 +93,7 @@ export function Component({image}: Props) { placeholderTextColor={pal.colors.textLight} value={altText} onChangeText={text => setAltText(enforceLen(text, MAX_ALT_TEXT))} - accessibilityLabel="Image alt text" + accessibilityLabel={_(msg`Image alt text`)} accessibilityHint="" accessibilityLabelledBy="imageAltText" autoFocus @@ -99,7 +102,7 @@ export function Component({image}: Props) { <TouchableOpacity testID="altTextImageSaveBtn" onPress={onPressSave} - accessibilityLabel="Save alt text" + accessibilityLabel={_(msg`Save alt text`)} accessibilityHint={`Saves alt text, which reads: ${altText}`} accessibilityRole="button"> <LinearGradient @@ -108,7 +111,7 @@ export function Component({image}: Props) { end={{x: 1, y: 1}} style={[styles.button]}> <Text type="button-lg" style={[s.white, s.bold]}> - Save + <Trans>Save</Trans> </Text> </LinearGradient> </TouchableOpacity> @@ -116,12 +119,12 @@ export function Component({image}: Props) { testID="altTextImageCancelBtn" onPress={onPressCancel} accessibilityRole="button" - accessibilityLabel="Cancel add image alt text" + accessibilityLabel={_(msg`Cancel add image alt text`)} accessibilityHint="" onAccessibilityEscape={onPressCancel}> <View style={[styles.button]}> <Text type="button-lg" style={[pal.textLight]}> - Cancel + <Trans>Cancel</Trans> </Text> </View> </TouchableOpacity> diff --git a/src/view/com/modals/BirthDateSettings.tsx b/src/view/com/modals/BirthDateSettings.tsx index 7b0778f83..6655b7a6b 100644 --- a/src/view/com/modals/BirthDateSettings.tsx +++ b/src/view/com/modals/BirthDateSettings.tsx @@ -15,6 +15,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {isWeb} from 'platform/detection' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {cleanError} from 'lib/strings/errors' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['50%'] @@ -22,6 +24,7 @@ export const snapPoints = ['50%'] export const Component = observer(function Component({}: {}) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const {closeModal} = useModalControls() const [date, setDate] = useState<Date>( store.preferences.birthDate || new Date(), @@ -49,12 +52,12 @@ export const Component = observer(function Component({}: {}) { style={[pal.view, styles.container, isMobile && {paddingHorizontal: 18}]}> <View style={styles.titleSection}> <Text type="title-lg" style={[pal.text, styles.title]}> - My Birthday + <Trans>My Birthday</Trans> </Text> </View> <Text type="lg" style={[pal.textLight, {marginBottom: 10}]}> - This information is not shared with other users. + <Trans>This information is not shared with other users.</Trans> </Text> <View> @@ -65,7 +68,7 @@ export const Component = observer(function Component({}: {}) { buttonType="default-light" buttonStyle={[pal.border, styles.dateInputButton]} buttonLabelType="lg" - accessibilityLabel="Birthday" + accessibilityLabel={_(msg`Birthday`)} accessibilityHint="Enter your birth date" accessibilityLabelledBy="birthDate" /> @@ -86,9 +89,11 @@ export const Component = observer(function Component({}: {}) { onPress={onSave} style={styles.btn} accessibilityRole="button" - accessibilityLabel="Save" + accessibilityLabel={_(msg`Save`)} accessibilityHint=""> - <Text style={[s.white, s.bold, s.f18]}>Save</Text> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Save</Trans> + </Text> </TouchableOpacity> )} </View> diff --git a/src/view/com/modals/ChangeEmail.tsx b/src/view/com/modals/ChangeEmail.tsx index ec37aeede..710c0588e 100644 --- a/src/view/com/modals/ChangeEmail.tsx +++ b/src/view/com/modals/ChangeEmail.tsx @@ -12,6 +12,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {isWeb} from 'platform/detection' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {cleanError} from 'lib/strings/errors' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' enum Stages { @@ -25,6 +27,7 @@ export const snapPoints = ['90%'] export const Component = observer(function Component({}: {}) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const [stage, setStage] = useState<Stages>(Stages.InputEmail) const [email, setEmail] = useState<string>( store.session.currentSession?.email || '', @@ -62,7 +65,9 @@ export const Component = observer(function Component({}: {}) { // you can remove this any time after Oct2023 // -prf if (err === 'email must be confirmed (temporary)') { - err = `Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed.` + err = _( + msg`Please confirm your email before changing it. This is a temporary requirement while email-updating tools are added, and it will soon be removed.`, + ) } setError(err) } finally { @@ -103,26 +108,26 @@ export const Component = observer(function Component({}: {}) { style={[s.flex1, isMobile && {paddingHorizontal: 18}]}> <View style={styles.titleSection}> <Text type="title-lg" style={[pal.text, styles.title]}> - {stage === Stages.InputEmail ? 'Change Your Email' : ''} - {stage === Stages.ConfirmCode ? 'Security Step Required' : ''} - {stage === Stages.Done ? 'Email Updated' : ''} + {stage === Stages.InputEmail ? _(msg`Change Your Email`) : ''} + {stage === Stages.ConfirmCode ? _(msg`Security Step Required`) : ''} + {stage === Stages.Done ? _(msg`Email Updated`) : ''} </Text> </View> <Text type="lg" style={[pal.textLight, {marginBottom: 10}]}> {stage === Stages.InputEmail ? ( - <>Enter your new email address below.</> + <Trans>Enter your new email address below.</Trans> ) : stage === Stages.ConfirmCode ? ( - <> + <Trans> An email has been sent to your previous address,{' '} {store.session.currentSession?.email || ''}. It includes a confirmation code which you can enter below. - </> + </Trans> ) : ( - <> + <Trans> Your email has been updated but not verified. As a next step, please verify your new email. - </> + </Trans> )} </Text> @@ -135,7 +140,7 @@ export const Component = observer(function Component({}: {}) { value={email} onChangeText={setEmail} accessible={true} - accessibilityLabel="Email" + accessibilityLabel={_(msg`Email`)} accessibilityHint="" autoCapitalize="none" autoComplete="email" @@ -151,7 +156,7 @@ export const Component = observer(function Component({}: {}) { value={confirmationCode} onChangeText={setConfirmationCode} accessible={true} - accessibilityLabel="Confirmation code" + accessibilityLabel={_(msg`Confirmation code`)} accessibilityHint="" autoCapitalize="none" autoComplete="off" @@ -175,9 +180,9 @@ export const Component = observer(function Component({}: {}) { testID="requestChangeBtn" type="primary" onPress={onRequestChange} - accessibilityLabel="Request Change" + accessibilityLabel={_(msg`Request Change`)} accessibilityHint="" - label="Request Change" + label={_(msg`Request Change`)} labelContainerStyle={{justifyContent: 'center', padding: 4}} labelStyle={[s.f18]} /> @@ -187,9 +192,9 @@ export const Component = observer(function Component({}: {}) { testID="confirmBtn" type="primary" onPress={onConfirm} - accessibilityLabel="Confirm Change" + accessibilityLabel={_(msg`Confirm Change`)} accessibilityHint="" - label="Confirm Change" + label={_(msg`Confirm Change`)} labelContainerStyle={{justifyContent: 'center', padding: 4}} labelStyle={[s.f18]} /> @@ -199,9 +204,9 @@ export const Component = observer(function Component({}: {}) { testID="verifyBtn" type="primary" onPress={onVerify} - accessibilityLabel="Verify New Email" + accessibilityLabel={_(msg`Verify New Email`)} accessibilityHint="" - label="Verify New Email" + label={_(msg`Verify New Email`)} labelContainerStyle={{justifyContent: 'center', padding: 4}} labelStyle={[s.f18]} /> @@ -210,9 +215,9 @@ export const Component = observer(function Component({}: {}) { testID="cancelBtn" type="default" onPress={() => closeModal()} - accessibilityLabel="Cancel" + accessibilityLabel={_(msg`Cancel`)} accessibilityHint="" - label="Cancel" + label={_(msg`Cancel`)} labelContainerStyle={{justifyContent: 'center', padding: 4}} labelStyle={[s.f18]} /> diff --git a/src/view/com/modals/ChangeHandle.tsx b/src/view/com/modals/ChangeHandle.tsx index 6184cb3b7..c03ebafda 100644 --- a/src/view/com/modals/ChangeHandle.tsx +++ b/src/view/com/modals/ChangeHandle.tsx @@ -22,6 +22,8 @@ import {useTheme} from 'lib/ThemeContext' import {useAnalytics} from 'lib/analytics/analytics' import {cleanError} from 'lib/strings/errors' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['100%'] @@ -31,6 +33,7 @@ export function Component({onChanged}: {onChanged: () => void}) { const [error, setError] = useState<string>('') const pal = usePalette('default') const {track} = useAnalytics() + const {_} = useLingui() const {closeModal} = useModalControls() const [isProcessing, setProcessing] = useState<boolean>(false) @@ -141,7 +144,7 @@ export function Component({onChanged}: {onChanged: () => void}) { <TouchableOpacity onPress={onPressCancel} accessibilityRole="button" - accessibilityLabel="Cancel change handle" + accessibilityLabel={_(msg`Cancel change handle`)} accessibilityHint="Exits handle change process" onAccessibilityEscape={onPressCancel}> <Text type="lg" style={pal.textLight}> @@ -153,7 +156,7 @@ export function Component({onChanged}: {onChanged: () => void}) { type="2xl-bold" style={[styles.titleMiddle, pal.text]} numberOfLines={1}> - Change Handle + <Trans>Change Handle</Trans> </Text> <View style={styles.titleRight}> {isProcessing ? ( @@ -163,7 +166,7 @@ export function Component({onChanged}: {onChanged: () => void}) { testID="retryConnectButton" onPress={onPressRetryConnect} accessibilityRole="button" - accessibilityLabel="Retry change handle" + accessibilityLabel={_(msg`Retry change handle`)} accessibilityHint={`Retries handle change to ${handle}`}> <Text type="xl-bold" style={[pal.link, s.pr5]}> Retry @@ -173,10 +176,10 @@ export function Component({onChanged}: {onChanged: () => void}) { <TouchableOpacity onPress={onPressSave} accessibilityRole="button" - accessibilityLabel="Save handle change" + accessibilityLabel={_(msg`Save handle change`)} accessibilityHint={`Saves handle change to ${handle}`}> <Text type="2xl-medium" style={pal.link}> - Save + <Trans>Save</Trans> </Text> </TouchableOpacity> ) : undefined} @@ -234,6 +237,7 @@ function ProvidedHandleForm({ }) { const pal = usePalette('default') const theme = useTheme() + const {_} = useLingui() // events // = @@ -266,12 +270,12 @@ function ProvidedHandleForm({ onChangeText={onChangeHandle} editable={!isProcessing} accessible={true} - accessibilityLabel="Handle" + accessibilityLabel={_(msg`Handle`)} accessibilityHint="Sets Bluesky username" /> </View> <Text type="md" style={[pal.textLight, s.pl10, s.pt10]}> - Your full handle will be{' '} + <Trans>Your full handle will be </Trans> <Text type="md-bold" style={pal.textLight}> @{createFullHandle(handle, userDomain)} </Text> @@ -280,9 +284,9 @@ function ProvidedHandleForm({ onPress={onToggleCustom} accessibilityRole="button" accessibilityHint="Hosting provider" - accessibilityLabel="Opens modal for using custom domain"> + accessibilityLabel={_(msg`Opens modal for using custom domain`)}> <Text type="md-medium" style={[pal.link, s.pl10, s.pt5]}> - I have my own domain + <Trans>I have my own domain</Trans> </Text> </TouchableOpacity> </> @@ -314,6 +318,7 @@ function CustomHandleForm({ const palSecondary = usePalette('secondary') const palError = usePalette('error') const theme = useTheme() + const {_} = useLingui() const [isVerifying, setIsVerifying] = React.useState(false) const [error, setError] = React.useState<string>('') const [isDNSForm, setDNSForm] = React.useState<boolean>(true) @@ -367,7 +372,7 @@ function CustomHandleForm({ return ( <> <Text type="md" style={[pal.text, s.pb5, s.pl5]} nativeID="customDomain"> - Enter the domain you want to use + <Trans>Enter the domain you want to use</Trans> </Text> <View style={[pal.btn, styles.textInputWrapper]}> <FontAwesomeIcon @@ -385,7 +390,7 @@ function CustomHandleForm({ onChangeText={onChangeHandle} editable={!isProcessing} accessibilityLabelledBy="customDomain" - accessibilityLabel="Custom domain" + accessibilityLabel={_(msg`Custom domain`)} accessibilityHint="Input your preferred hosting provider" /> </View> @@ -413,7 +418,7 @@ function CustomHandleForm({ {isDNSForm ? ( <> <Text type="md" style={[pal.text, s.pb5, s.pl5]}> - Add the following DNS record to your domain: + <Trans>Add the following DNS record to your domain:</Trans> </Text> <View style={[styles.dnsTable, pal.btn]}> <Text type="md-medium" style={[styles.dnsLabel, pal.text]}> @@ -451,7 +456,7 @@ function CustomHandleForm({ ) : ( <> <Text type="md" style={[pal.text, s.pb5, s.pl5]}> - Upload a text file to: + <Trans>Upload a text file to:</Trans> </Text> <View style={[styles.valueContainer, pal.btn]}> <View style={[styles.dnsValue]}> @@ -483,7 +488,7 @@ function CustomHandleForm({ {canSave === true && ( <View style={[styles.message, palSecondary.view]}> <Text type="md-medium" style={palSecondary.text}> - Domain verified! + <Trans>Domain verified!</Trans> </Text> </View> )} @@ -511,7 +516,7 @@ function CustomHandleForm({ <View style={styles.spacer} /> <TouchableOpacity onPress={onToggleCustom} - accessibilityLabel="Use default provider" + accessibilityLabel={_(msg`Use default provider`)} accessibilityHint="Use bsky.social as hosting provider"> <Text type="md-medium" style={[pal.link, s.pl10, s.pt5]}> Nevermind, create a handle for me diff --git a/src/view/com/modals/Confirm.tsx b/src/view/com/modals/Confirm.tsx index 6b942057b..5e869f396 100644 --- a/src/view/com/modals/Confirm.tsx +++ b/src/view/com/modals/Confirm.tsx @@ -11,6 +11,8 @@ import {ErrorMessage} from '../util/error/ErrorMessage' import {cleanError} from 'lib/strings/errors' import {usePalette} from 'lib/hooks/usePalette' import {isWeb} from 'platform/detection' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import type {ConfirmModal} from '#/state/modals' import {useModalControls} from '#/state/modals' @@ -26,6 +28,7 @@ export function Component({ cancelBtnText, }: ConfirmModal) { const pal = usePalette('default') + const {_} = useLingui() const {closeModal} = useModalControls() const [isProcessing, setIsProcessing] = useState<boolean>(false) const [error, setError] = useState<string>('') @@ -69,7 +72,7 @@ export function Component({ onPress={onPress} style={[styles.btn, confirmBtnStyle]} accessibilityRole="button" - accessibilityLabel="Confirm" + accessibilityLabel={_(msg`Confirm`)} accessibilityHint=""> <Text style={[s.white, s.bold, s.f18]}> {confirmBtnText ?? 'Confirm'} @@ -82,7 +85,7 @@ export function Component({ onPress={onPressCancel} style={[styles.btnCancel, s.mt10]} accessibilityRole="button" - accessibilityLabel="Cancel" + accessibilityLabel={_(msg`Cancel`)} accessibilityHint=""> <Text type="button-lg" style={pal.textLight}> {cancelBtnText ?? 'Cancel'} diff --git a/src/view/com/modals/ContentFilteringSettings.tsx b/src/view/com/modals/ContentFilteringSettings.tsx index 0891a6473..ad4a0fa52 100644 --- a/src/view/com/modals/ContentFilteringSettings.tsx +++ b/src/view/com/modals/ContentFilteringSettings.tsx @@ -16,6 +16,8 @@ import {isIOS} from 'platform/detection' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import * as Toast from '../util/Toast' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['90%'] @@ -25,6 +27,7 @@ export const Component = observer( const store = useStores() const {isMobile} = useWebMediaQueries() const pal = usePalette('default') + const {_} = useLingui() const {closeModal} = useModalControls() React.useEffect(() => { @@ -37,7 +40,9 @@ export const Component = observer( return ( <View testID="contentFilteringModal" style={[pal.view, styles.container]}> - <Text style={[pal.text, styles.title]}>Content Filtering</Text> + <Text style={[pal.text, styles.title]}> + <Trans>Content Filtering</Trans> + </Text> <ScrollView style={styles.scrollContainer}> <AdultContentEnabledPref /> <ContentLabelPref @@ -71,14 +76,16 @@ export const Component = observer( testID="sendReportBtn" onPress={onPressDone} accessibilityRole="button" - accessibilityLabel="Done" + accessibilityLabel={_(msg`Done`)} accessibilityHint=""> <LinearGradient colors={[gradients.blueLight.start, gradients.blueLight.end]} start={{x: 0, y: 0}} end={{x: 1, y: 1}} style={[styles.btn]}> - <Text style={[s.white, s.bold, s.f18]}>Done</Text> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Done</Trans> + </Text> </LinearGradient> </Pressable> </View> diff --git a/src/view/com/modals/CreateOrEditList.tsx b/src/view/com/modals/CreateOrEditList.tsx index cdad37770..cfd0f7569 100644 --- a/src/view/com/modals/CreateOrEditList.tsx +++ b/src/view/com/modals/CreateOrEditList.tsx @@ -24,6 +24,8 @@ import {useTheme} from 'lib/ThemeContext' import {useAnalytics} from 'lib/analytics/analytics' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {cleanError, isNetworkError} from 'lib/strings/errors' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' const MAX_NAME = 64 // todo @@ -47,6 +49,7 @@ export function Component({ const pal = usePalette('default') const theme = useTheme() const {track} = useAnalytics() + const {_} = useLingui() const activePurpose = useMemo(() => { if (list?.data?.purpose) { @@ -164,14 +167,18 @@ export function Component({ ]} testID="createOrEditListModal"> <Text style={[styles.title, pal.text]}> - {list ? 'Edit' : 'New'} {purposeLabel} List + <Trans> + {list ? 'Edit' : 'New'} {purposeLabel} List + </Trans> </Text> {error !== '' && ( <View style={styles.errorContainer}> <ErrorMessage message={error} /> </View> )} - <Text style={[styles.label, pal.text]}>List Avatar</Text> + <Text style={[styles.label, pal.text]}> + <Trans>List Avatar</Trans> + </Text> <View style={[styles.avi, {borderColor: pal.colors.background}]}> <EditableUserAvatar type="list" @@ -183,7 +190,7 @@ export function Component({ <View style={styles.form}> <View> <Text style={[styles.label, pal.text]} nativeID="list-name"> - List Name + <Trans>List Name</Trans> </Text> <TextInput testID="editNameInput" @@ -195,14 +202,14 @@ export function Component({ value={name} onChangeText={v => setName(enforceLen(v, MAX_NAME))} accessible={true} - accessibilityLabel="Name" + accessibilityLabel={_(msg`Name`)} accessibilityHint="" accessibilityLabelledBy="list-name" /> </View> <View style={s.pb10}> <Text style={[styles.label, pal.text]} nativeID="list-description"> - Description + <Trans>Description</Trans> </Text> <TextInput testID="editDescriptionInput" @@ -218,7 +225,7 @@ export function Component({ value={description} onChangeText={v => setDescription(enforceLen(v, MAX_DESCRIPTION))} accessible={true} - accessibilityLabel="Description" + accessibilityLabel={_(msg`Description`)} accessibilityHint="" accessibilityLabelledBy="list-description" /> @@ -233,14 +240,16 @@ export function Component({ style={s.mt10} onPress={onPressSave} accessibilityRole="button" - accessibilityLabel="Save" + accessibilityLabel={_(msg`Save`)} accessibilityHint=""> <LinearGradient colors={[gradients.blueLight.start, gradients.blueLight.end]} start={{x: 0, y: 0}} end={{x: 1, y: 1}} style={[styles.btn]}> - <Text style={[s.white, s.bold]}>Save</Text> + <Text style={[s.white, s.bold]}> + <Trans>Save</Trans> + </Text> </LinearGradient> </TouchableOpacity> )} @@ -249,11 +258,13 @@ export function Component({ style={s.mt5} onPress={onPressCancel} accessibilityRole="button" - accessibilityLabel="Cancel" + accessibilityLabel={_(msg`Cancel`)} accessibilityHint="" onAccessibilityEscape={onPressCancel}> <View style={[styles.btn]}> - <Text style={[s.black, s.bold, pal.text]}>Cancel</Text> + <Text style={[s.black, s.bold, pal.text]}> + <Trans>Cancel</Trans> + </Text> </View> </TouchableOpacity> </View> diff --git a/src/view/com/modals/DeleteAccount.tsx b/src/view/com/modals/DeleteAccount.tsx index 9a8a8b4b0..624dbda2d 100644 --- a/src/view/com/modals/DeleteAccount.tsx +++ b/src/view/com/modals/DeleteAccount.tsx @@ -17,6 +17,8 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {ErrorMessage} from '../util/error/ErrorMessage' import {cleanError} from 'lib/strings/errors' import {resetToTab} from '../../../Navigation' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['60%'] @@ -25,6 +27,7 @@ export function Component({}: {}) { const pal = usePalette('default') const theme = useTheme() const store = useStores() + const {_} = useLingui() const {closeModal} = useModalControls() const {isMobile} = useWebMediaQueries() const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false) @@ -71,7 +74,7 @@ export function Component({}: {}) { <View style={[styles.innerContainer, pal.view]}> <View style={[styles.titleContainer, pal.view]}> <Text type="title-xl" style={[s.textCenter, pal.text]}> - Delete Account + <Trans>Delete Account</Trans> </Text> <View style={[pal.view, s.flexRow]}> <Text type="title-xl" style={[pal.text, s.bold]}> @@ -95,8 +98,10 @@ export function Component({}: {}) { {!isEmailSent ? ( <> <Text type="lg" style={[styles.description, pal.text]}> - For security reasons, we'll need to send a confirmation code to - your email address. + <Trans> + For security reasons, we'll need to send a confirmation code to + your email address. + </Trans> </Text> {error ? ( <View style={s.mt10}> @@ -113,7 +118,7 @@ export function Component({}: {}) { style={styles.mt20} onPress={onPressSendEmail} accessibilityRole="button" - accessibilityLabel="Send email" + accessibilityLabel={_(msg`Send email`)} accessibilityHint="Sends email with confirmation code for account deletion"> <LinearGradient colors={[ @@ -124,7 +129,7 @@ export function Component({}: {}) { end={{x: 1, y: 1}} style={[styles.btn]}> <Text type="button-lg" style={[s.white, s.bold]}> - Send Email + <Trans>Send Email</Trans> </Text> </LinearGradient> </TouchableOpacity> @@ -132,11 +137,11 @@ export function Component({}: {}) { style={[styles.btn, s.mt10]} onPress={onCancel} accessibilityRole="button" - accessibilityLabel="Cancel account deletion" + accessibilityLabel={_(msg`Cancel account deletion`)} accessibilityHint="" onAccessibilityEscape={onCancel}> <Text type="button-lg" style={pal.textLight}> - Cancel + <Trans>Cancel</Trans> </Text> </TouchableOpacity> </> @@ -149,8 +154,10 @@ export function Component({}: {}) { type="lg" style={styles.description} nativeID="confirmationCode"> - Check your inbox for an email with the confirmation code to enter - below: + <Trans> + Check your inbox for an email with the confirmation code to + enter below: + </Trans> </Text> <TextInput style={[styles.textInput, pal.borderDark, pal.text, styles.mb20]} @@ -160,11 +167,11 @@ export function Component({}: {}) { value={confirmCode} onChangeText={setConfirmCode} accessibilityLabelledBy="confirmationCode" - accessibilityLabel="Confirmation code" + accessibilityLabel={_(msg`Confirmation code`)} accessibilityHint="Input confirmation code for account deletion" /> <Text type="lg" style={styles.description} nativeID="password"> - Please enter your password as well: + <Trans>Please enter your password as well:</Trans> </Text> <TextInput style={[styles.textInput, pal.borderDark, pal.text]} @@ -175,7 +182,7 @@ export function Component({}: {}) { value={password} onChangeText={setPassword} accessibilityLabelledBy="password" - accessibilityLabel="Password" + accessibilityLabel={_(msg`Password`)} accessibilityHint="Input password for account deletion" /> {error ? ( @@ -193,21 +200,21 @@ export function Component({}: {}) { style={[styles.btn, styles.evilBtn, styles.mt20]} onPress={onPressConfirmDelete} accessibilityRole="button" - accessibilityLabel="Confirm delete account" + accessibilityLabel={_(msg`Confirm delete account`)} accessibilityHint=""> <Text type="button-lg" style={[s.white, s.bold]}> - Delete my account + <Trans>Delete my account</Trans> </Text> </TouchableOpacity> <TouchableOpacity style={[styles.btn, s.mt10]} onPress={onCancel} accessibilityRole="button" - accessibilityLabel="Cancel account deletion" + accessibilityLabel={_(msg`Cancel account deletion`)} accessibilityHint="Exits account deletion process" onAccessibilityEscape={onCancel}> <Text type="button-lg" style={pal.textLight}> - Cancel + <Trans>Cancel</Trans> </Text> </TouchableOpacity> </> diff --git a/src/view/com/modals/EditImage.tsx b/src/view/com/modals/EditImage.tsx index a2a458f4c..753907472 100644 --- a/src/view/com/modals/EditImage.tsx +++ b/src/view/com/modals/EditImage.tsx @@ -18,6 +18,8 @@ import {Slider} from '@miblanchard/react-native-slider' import {MaterialIcons} from '@expo/vector-icons' import {observer} from 'mobx-react-lite' import {getKeys} from 'lib/type-assertions' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['80%'] @@ -52,6 +54,7 @@ export const Component = observer(function EditImageImpl({ }: Props) { const pal = usePalette('default') const theme = useTheme() + const {_} = useLingui() const windowDimensions = useWindowDimensions() const {isMobile} = useWebMediaQueries() const {closeModal} = useModalControls() @@ -200,7 +203,9 @@ export const Component = observer(function EditImageImpl({ paddingHorizontal: isMobile ? 16 : undefined, }, ]}> - <Text style={[styles.title, pal.text]}>Edit image</Text> + <Text style={[styles.title, pal.text]}> + <Trans>Edit image</Trans> + </Text> <View style={[styles.gap18, s.flexRow]}> <View> <View @@ -228,7 +233,7 @@ export const Component = observer(function EditImageImpl({ <View> {!isMobile ? ( <Text type="sm-bold" style={pal.text}> - Ratios + <Trans>Ratios</Trans> </Text> ) : null} <View style={imgControlStyles}> @@ -263,7 +268,7 @@ export const Component = observer(function EditImageImpl({ </View> {!isMobile ? ( <Text type="sm-bold" style={[pal.text, styles.subsection]}> - Transformations + <Trans>Transformations</Trans> </Text> ) : null} <View style={imgControlStyles}> @@ -291,7 +296,7 @@ export const Component = observer(function EditImageImpl({ </View> <View style={[styles.gap18, styles.bottomSection, pal.border]}> <Text type="sm-bold" style={pal.text} nativeID="alt-text"> - Accessibility + <Trans>Accessibility</Trans> </Text> <TextInput testID="altTextImageInput" @@ -307,7 +312,7 @@ export const Component = observer(function EditImageImpl({ multiline value={altText} onChangeText={text => setAltText(enforceLen(text, MAX_ALT_TEXT))} - accessibilityLabel="Alt text" + accessibilityLabel={_(msg`Alt text`)} accessibilityHint="" accessibilityLabelledBy="alt-text" /> @@ -315,7 +320,7 @@ export const Component = observer(function EditImageImpl({ <View style={styles.btns}> <Pressable onPress={onPressCancel} accessibilityRole="button"> <Text type="xl" style={pal.link}> - Cancel + <Trans>Cancel</Trans> </Text> </Pressable> <Pressable onPress={onPressSave} accessibilityRole="button"> @@ -325,7 +330,7 @@ export const Component = observer(function EditImageImpl({ end={{x: 1, y: 1}} style={[styles.btn]}> <Text type="xl-medium" style={s.white}> - Done + <Trans>Done</Trans> </Text> </LinearGradient> </Pressable> diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx index f08bb2326..eef408e98 100644 --- a/src/view/com/modals/EditProfile.tsx +++ b/src/view/com/modals/EditProfile.tsx @@ -26,6 +26,8 @@ import {useAnalytics} from 'lib/analytics/analytics' import {cleanError, isNetworkError} from 'lib/strings/errors' import Animated, {FadeOut} from 'react-native-reanimated' import {isWeb} from 'platform/detection' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' const AnimatedTouchableOpacity = @@ -44,6 +46,7 @@ export function Component({ const pal = usePalette('default') const theme = useTheme() const {track} = useAnalytics() + const {_} = useLingui() const {closeModal} = useModalControls() const [isProcessing, setProcessing] = useState<boolean>(false) @@ -151,7 +154,9 @@ export function Component({ return ( <KeyboardAvoidingView style={s.flex1} behavior="height"> <ScrollView style={[pal.view]} testID="editProfileModal"> - <Text style={[styles.title, pal.text]}>Edit my profile</Text> + <Text style={[styles.title, pal.text]}> + <Trans>Edit my profile</Trans> + </Text> <View style={styles.photos}> <UserBanner banner={userBanner} @@ -172,7 +177,9 @@ export function Component({ )} <View style={styles.form}> <View> - <Text style={[styles.label, pal.text]}>Display Name</Text> + <Text style={[styles.label, pal.text]}> + <Trans>Display Name</Trans> + </Text> <TextInput testID="editProfileDisplayNameInput" style={[styles.textInput, pal.border, pal.text]} @@ -183,12 +190,14 @@ export function Component({ setDisplayName(enforceLen(v, MAX_DISPLAY_NAME)) } accessible={true} - accessibilityLabel="Display name" + accessibilityLabel={_(msg`Display name`)} accessibilityHint="Edit your display name" /> </View> <View style={s.pb10}> - <Text style={[styles.label, pal.text]}>Description</Text> + <Text style={[styles.label, pal.text]}> + <Trans>Description</Trans> + </Text> <TextInput testID="editProfileDescriptionInput" style={[styles.textArea, pal.border, pal.text]} @@ -199,7 +208,7 @@ export function Component({ value={description} onChangeText={v => setDescription(enforceLen(v, MAX_DESCRIPTION))} accessible={true} - accessibilityLabel="Description" + accessibilityLabel={_(msg`Description`)} accessibilityHint="Edit your profile description" /> </View> @@ -213,14 +222,16 @@ export function Component({ style={s.mt10} onPress={onPressSave} accessibilityRole="button" - accessibilityLabel="Save" + accessibilityLabel={_(msg`Save`)} accessibilityHint="Saves any changes to your profile"> <LinearGradient colors={[gradients.blueLight.start, gradients.blueLight.end]} start={{x: 0, y: 0}} end={{x: 1, y: 1}} style={[styles.btn]}> - <Text style={[s.white, s.bold]}>Save Changes</Text> + <Text style={[s.white, s.bold]}> + <Trans>Save Changes</Trans> + </Text> </LinearGradient> </TouchableOpacity> )} @@ -231,11 +242,13 @@ export function Component({ style={s.mt5} onPress={onPressCancel} accessibilityRole="button" - accessibilityLabel="Cancel profile editing" + accessibilityLabel={_(msg`Cancel profile editing`)} accessibilityHint="" onAccessibilityEscape={onPressCancel}> <View style={[styles.btn]}> - <Text style={[s.black, s.bold, pal.text]}>Cancel</Text> + <Text style={[s.black, s.bold, pal.text]}> + <Trans>Cancel</Trans> + </Text> </View> </AnimatedTouchableOpacity> )} diff --git a/src/view/com/modals/InviteCodes.tsx b/src/view/com/modals/InviteCodes.tsx index 227b25275..a90a9eab6 100644 --- a/src/view/com/modals/InviteCodes.tsx +++ b/src/view/com/modals/InviteCodes.tsx @@ -15,6 +15,7 @@ import {ScrollView} from './util' import {usePalette} from 'lib/hooks/usePalette' import {isWeb} from 'platform/detection' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Trans} from '@lingui/macro' import {useModalControls} from '#/state/modals' import {useInvitesState, useInvitesAPI} from '#/state/invites' import {UserInfoText} from '../util/UserInfoText' @@ -38,8 +39,10 @@ export function Component({}: {}) { <View style={[styles.container, pal.view]} testID="inviteCodesModal"> <View style={[styles.empty, pal.viewLight]}> <Text type="lg" style={[pal.text, styles.emptyText]}> - You don't have any invite codes yet! We'll send you some when you've - been on Bluesky for a little longer. + <Trans> + You don't have any invite codes yet! We'll send you some when + you've been on Bluesky for a little longer. + </Trans> </Text> </View> <View style={styles.flex1} /> @@ -63,10 +66,12 @@ export function Component({}: {}) { return ( <View style={[styles.container, pal.view]} testID="inviteCodesModal"> <Text type="title-xl" style={[styles.title, pal.text]}> - Invite a Friend + <Trans>Invite a Friend</Trans> </Text> <Text type="lg" style={[styles.description, pal.text]}> - Each code works once. You'll receive more invite codes periodically. + <Trans> + Each code works once. You'll receive more invite codes periodically. + </Trans> </Text> <ScrollView style={[styles.scrollContainer, pal.border]}> {store.me.invites.map((invite, i) => ( @@ -138,7 +143,9 @@ const InviteCode = observer(function InviteCodeImpl({ </Text> <View style={styles.flex1} /> {!used && invitesState.copiedInvites.includes(invite.code) && ( - <Text style={[pal.textLight, styles.codeCopied]}>Copied</Text> + <Text style={[pal.textLight, styles.codeCopied]}> + <Trans>Copied</Trans> + </Text> )} {!used && ( <FontAwesomeIcon @@ -154,7 +161,9 @@ const InviteCode = observer(function InviteCodeImpl({ gap: 8, paddingTop: 6, }}> - <Text style={pal.text}>Used by:</Text> + <Text style={pal.text}> + <Trans>Used by:</Trans> + </Text> {invite.uses.map(use => ( <Link key={use.usedBy} diff --git a/src/view/com/modals/LinkWarning.tsx b/src/view/com/modals/LinkWarning.tsx index 751c69b3f..cb86387a0 100644 --- a/src/view/com/modals/LinkWarning.tsx +++ b/src/view/com/modals/LinkWarning.tsx @@ -10,6 +10,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {isWeb} from 'platform/detection' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {isPossiblyAUrl, splitApexDomain} from 'lib/strings/url-helpers' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['50%'] @@ -24,6 +26,7 @@ export const Component = observer(function Component({ const pal = usePalette('default') const {closeModal} = useModalControls() const {isMobile} = useWebMediaQueries() + const {_} = useLingui() const potentiallyMisleading = isPossiblyAUrl(text) const onPressVisit = () => { @@ -45,26 +48,26 @@ export const Component = observer(function Component({ size={18} /> <Text type="title-lg" style={[pal.text, styles.title]}> - Potentially Misleading Link + <Trans>Potentially Misleading Link</Trans> </Text> </> ) : ( <Text type="title-lg" style={[pal.text, styles.title]}> - Leaving Bluesky + <Trans>Leaving Bluesky</Trans> </Text> )} </View> <View style={{gap: 10}}> <Text type="lg" style={pal.text}> - This link is taking you to the following website: + <Trans>This link is taking you to the following website:</Trans> </Text> <LinkBox href={href} /> {potentiallyMisleading && ( <Text type="lg" style={pal.text}> - Make sure this is where you intend to go! + <Trans>Make sure this is where you intend to go!</Trans> </Text> )} </View> @@ -74,7 +77,7 @@ export const Component = observer(function Component({ testID="confirmBtn" type="primary" onPress={onPressVisit} - accessibilityLabel="Visit Site" + accessibilityLabel={_(msg`Visit Site`)} accessibilityHint="" label="Visit Site" labelContainerStyle={{justifyContent: 'center', padding: 4}} @@ -84,7 +87,7 @@ export const Component = observer(function Component({ testID="cancelBtn" type="default" onPress={() => closeModal()} - accessibilityLabel="Cancel" + accessibilityLabel={_(msg`Cancel`)} accessibilityHint="" label="Cancel" labelContainerStyle={{justifyContent: 'center', padding: 4}} diff --git a/src/view/com/modals/ListAddUser.tsx b/src/view/com/modals/ListAddUser.tsx index 8864ebc78..e59ab90df 100644 --- a/src/view/com/modals/ListAddUser.tsx +++ b/src/view/com/modals/ListAddUser.tsx @@ -26,6 +26,8 @@ import {cleanError} from 'lib/strings/errors' import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {HITSLOP_20} from '#/lib/constants' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['90%'] @@ -39,6 +41,7 @@ export const Component = observer(function Component({ }) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const {closeModal} = useModalControls() const {isMobile} = useWebMediaQueries() const [query, setQuery] = useState('') @@ -85,7 +88,7 @@ export const Component = observer(function Component({ value={query} onChangeText={onChangeQuery} accessible={true} - accessibilityLabel="Search" + accessibilityLabel={_(msg`Search`)} accessibilityHint="" autoFocus autoCapitalize="none" @@ -97,7 +100,7 @@ export const Component = observer(function Component({ <Pressable onPress={onPressCancelSearch} accessibilityRole="button" - accessibilityLabel="Cancel search" + accessibilityLabel={_(msg`Cancel search`)} accessibilityHint="Exits inputting search query" onAccessibilityEscape={onPressCancelSearch} hitSlop={HITSLOP_20}> @@ -136,7 +139,7 @@ export const Component = observer(function Component({ pal.textLight, {paddingHorizontal: 12, paddingVertical: 16}, ]}> - No results found for {autocompleteView.prefix} + <Trans>No results found for {autocompleteView.prefix}</Trans> </Text> )} </ScrollView> @@ -149,7 +152,7 @@ export const Component = observer(function Component({ testID="doneBtn" type="default" onPress={() => closeModal()} - accessibilityLabel="Done" + accessibilityLabel={_(msg`Done`)} accessibilityHint="" label="Done" labelContainerStyle={{justifyContent: 'center', padding: 4}} diff --git a/src/view/com/modals/Repost.tsx b/src/view/com/modals/Repost.tsx index 13728b62b..a72da29b4 100644 --- a/src/view/com/modals/Repost.tsx +++ b/src/view/com/modals/Repost.tsx @@ -6,6 +6,8 @@ import {Text} from '../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' import {RepostIcon} from 'lib/icons' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = [250] @@ -21,6 +23,7 @@ export function Component({ // TODO: Add author into component }) { const pal = usePalette('default') + const {_} = useLingui() const {closeModal} = useModalControls() const onPress = async () => { closeModal() @@ -38,7 +41,7 @@ export function Component({ accessibilityHint={isReposted ? 'Remove repost' : 'Repost '}> <RepostIcon strokeWidth={2} size={24} style={s.blue3} /> <Text type="title-lg" style={[styles.actionBtnLabel, pal.text]}> - {!isReposted ? 'Repost' : 'Undo repost'} + <Trans>{!isReposted ? 'Repost' : 'Undo repost'}</Trans> </Text> </TouchableOpacity> <TouchableOpacity @@ -46,11 +49,11 @@ export function Component({ style={[styles.actionBtn]} onPress={onQuote} accessibilityRole="button" - accessibilityLabel="Quote post" + accessibilityLabel={_(msg`Quote post`)} accessibilityHint=""> <FontAwesomeIcon icon="quote-left" size={24} style={s.blue3} /> <Text type="title-lg" style={[styles.actionBtnLabel, pal.text]}> - Quote Post + <Trans>Quote Post</Trans> </Text> </TouchableOpacity> </View> @@ -58,7 +61,7 @@ export function Component({ testID="cancelBtn" onPress={onPress} accessibilityRole="button" - accessibilityLabel="Cancel quote post" + accessibilityLabel={_(msg`Cancel quote post`)} accessibilityHint="" onAccessibilityEscape={onPress}> <LinearGradient @@ -66,7 +69,9 @@ export function Component({ start={{x: 0, y: 0}} end={{x: 1, y: 1}} style={[styles.btn]}> - <Text style={[s.white, s.bold, s.f18]}>Cancel</Text> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Cancel</Trans> + </Text> </LinearGradient> </TouchableOpacity> </View> diff --git a/src/view/com/modals/SelfLabel.tsx b/src/view/com/modals/SelfLabel.tsx index 242b6a38a..f8b64085d 100644 --- a/src/view/com/modals/SelfLabel.tsx +++ b/src/view/com/modals/SelfLabel.tsx @@ -9,6 +9,8 @@ import {isWeb} from 'platform/detection' import {Button} from '../util/forms/Button' import {SelectableBtn} from '../util/forms/SelectableBtn' import {ScrollView} from 'view/com/modals/util' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' const ADULT_CONTENT_LABELS = ['sexual', 'nudity', 'porn'] @@ -28,6 +30,7 @@ export const Component = observer(function Component({ const {closeModal} = useModalControls() const {isMobile} = useWebMediaQueries() const [selected, setSelected] = useState(labels) + const {_} = useLingui() const toggleAdultLabel = (label: string) => { const hadLabel = selected.includes(label) @@ -51,7 +54,7 @@ export const Component = observer(function Component({ <View testID="selfLabelModal" style={[pal.view, styles.container]}> <View style={styles.titleSection}> <Text type="title-lg" style={[pal.text, styles.title]}> - Add a content warning + <Trans>Add a content warning</Trans> </Text> </View> @@ -70,7 +73,7 @@ export const Component = observer(function Component({ paddingBottom: 8, }}> <Text type="title" style={pal.text}> - Adult Content + <Trans>Adult Content</Trans> </Text> {hasAdultSelection ? ( <Button @@ -78,7 +81,7 @@ export const Component = observer(function Component({ onPress={removeAdultLabel} style={{paddingTop: 0, paddingBottom: 0, paddingRight: 0}}> <Text type="md" style={pal.link}> - Remove + <Trans>Remove</Trans> </Text> </Button> ) : null} @@ -116,23 +119,25 @@ export const Component = observer(function Component({ <Text style={[pal.text, styles.adultExplainer]}> {selected.includes('sexual') ? ( - <>Pictures meant for adults.</> + <Trans>Pictures meant for adults.</Trans> ) : selected.includes('nudity') ? ( - <>Artistic or non-erotic nudity.</> + <Trans>Artistic or non-erotic nudity.</Trans> ) : selected.includes('porn') ? ( - <>Sexual activity or erotic nudity.</> + <Trans>Sexual activity or erotic nudity.</Trans> ) : ( - <>If none are selected, suitable for all ages.</> + <Trans>If none are selected, suitable for all ages.</Trans> )} </Text> </> ) : ( <View> <Text style={[pal.textLight]}> - <Text type="md-bold" style={[pal.textLight]}> - Not Applicable + <Text type="md-bold" style={[pal.textLight, s.mr5]}> + <Trans>Not Applicable.</Trans> </Text> - . This warning is only available for posts with media attached. + <Trans> + This warning is only available for posts with media attached. + </Trans> </Text> </View> )} @@ -147,9 +152,11 @@ export const Component = observer(function Component({ }} style={styles.btn} accessibilityRole="button" - accessibilityLabel="Confirm" + accessibilityLabel={_(msg`Confirm`)} accessibilityHint=""> - <Text style={[s.white, s.bold, s.f18]}>Done</Text> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Done</Trans> + </Text> </TouchableOpacity> </View> </View> diff --git a/src/view/com/modals/ServerInput.tsx b/src/view/com/modals/ServerInput.tsx index 0f8db30b6..09a460315 100644 --- a/src/view/com/modals/ServerInput.tsx +++ b/src/view/com/modals/ServerInput.tsx @@ -11,6 +11,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'state/index' import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['80%'] @@ -19,6 +21,7 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) { const theme = useTheme() const pal = usePalette('default') const [customUrl, setCustomUrl] = useState<string>('') + const {_} = useLingui() const {closeModal} = useModalControls() const doSelect = (url: string) => { @@ -32,7 +35,7 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) { return ( <View style={[pal.view, s.flex1]} testID="serverInputModal"> <Text type="2xl-bold" style={[pal.text, s.textCenter]}> - Choose Service + <Trans>Choose Service</Trans> </Text> <ScrollView style={styles.inner}> <View style={styles.group}> @@ -43,7 +46,9 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) { style={styles.btn} onPress={() => doSelect(LOCAL_DEV_SERVICE)} accessibilityRole="button"> - <Text style={styles.btnText}>Local dev server</Text> + <Text style={styles.btnText}> + <Trans>Local dev server</Trans> + </Text> <FontAwesomeIcon icon="arrow-right" style={s.white as FontAwesomeIconStyle} @@ -53,7 +58,9 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) { style={styles.btn} onPress={() => doSelect(STAGING_SERVICE)} accessibilityRole="button"> - <Text style={styles.btnText}>Staging</Text> + <Text style={styles.btnText}> + <Trans>Staging</Trans> + </Text> <FontAwesomeIcon icon="arrow-right" style={s.white as FontAwesomeIconStyle} @@ -65,9 +72,11 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) { style={styles.btn} onPress={() => doSelect(PROD_SERVICE)} accessibilityRole="button" - accessibilityLabel="Select Bluesky Social" + accessibilityLabel={_(msg`Select Bluesky Social`)} accessibilityHint="Sets Bluesky Social as your service provider"> - <Text style={styles.btnText}>Bluesky.Social</Text> + <Text style={styles.btnText}> + <Trans>Bluesky.Social</Trans> + </Text> <FontAwesomeIcon icon="arrow-right" style={s.white as FontAwesomeIconStyle} @@ -75,7 +84,9 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) { </TouchableOpacity> </View> <View style={styles.group}> - <Text style={[pal.text, styles.label]}>Other service</Text> + <Text style={[pal.text, styles.label]}> + <Trans>Other service</Trans> + </Text> <View style={s.flexRow}> <TextInput testID="customServerTextInput" @@ -88,7 +99,7 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) { keyboardAppearance={theme.colorScheme} value={customUrl} onChangeText={setCustomUrl} - accessibilityLabel="Custom domain" + accessibilityLabel={_(msg`Custom domain`)} // TODO: Simplify this wording further to be understandable by everyone accessibilityHint="Use your domain as your Bluesky client service provider" /> diff --git a/src/view/com/modals/SwitchAccount.tsx b/src/view/com/modals/SwitchAccount.tsx index d5fa32692..1d9457995 100644 --- a/src/view/com/modals/SwitchAccount.tsx +++ b/src/view/com/modals/SwitchAccount.tsx @@ -17,12 +17,15 @@ import {Link} from '../util/Link' import {makeProfileLink} from 'lib/routes/links' import {BottomSheetScrollView} from '@gorhom/bottom-sheet' import {Haptics} from 'lib/haptics' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export const snapPoints = ['40%', '90%'] export function Component({}: {}) { const pal = usePalette('default') const {track} = useAnalytics() + const {_: _lingui} = useLingui() const store = useStores() const [isSwitching, _, onPressSwitchAccount] = useAccountSwitcher() @@ -41,7 +44,7 @@ export function Component({}: {}) { style={[styles.container, pal.view]} contentContainerStyle={[styles.innerContainer, pal.view]}> <Text type="title-xl" style={[styles.title, pal.text]}> - Switch Account + <Trans>Switch Account</Trans> </Text> {isSwitching ? ( <View style={[pal.view, styles.linkCard]}> @@ -65,10 +68,10 @@ export function Component({}: {}) { testID="signOutBtn" onPress={isSwitching ? undefined : onPressSignout} accessibilityRole="button" - accessibilityLabel="Sign out" + accessibilityLabel={_lingui(msg`Sign out`)} accessibilityHint={`Signs ${store.me.displayName} out of Bluesky`}> <Text type="lg" style={pal.link}> - Sign out + <Trans>Sign out</Trans> </Text> </TouchableOpacity> </View> diff --git a/src/view/com/modals/UserAddRemoveLists.tsx b/src/view/com/modals/UserAddRemoveLists.tsx index f86e88439..efcfc43be 100644 --- a/src/view/com/modals/UserAddRemoveLists.tsx +++ b/src/view/com/modals/UserAddRemoveLists.tsx @@ -21,6 +21,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {isWeb, isAndroid} from 'platform/detection' import isEqual from 'lodash.isequal' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['fullscreen'] @@ -39,6 +41,7 @@ export const Component = observer(function UserAddRemoveListsImpl({ const store = useStores() const {closeModal} = useModalControls() const pal = usePalette('default') + const {_} = useLingui() const palPrimary = usePalette('primary') const palInverted = usePalette('inverted') const [originalSelections, setOriginalSelections] = React.useState<string[]>( @@ -181,7 +184,7 @@ export const Component = observer(function UserAddRemoveListsImpl({ return ( <View testID="userAddRemoveListsModal" style={s.hContentRegion}> <Text style={[styles.title, pal.text]}> - Update {displayName} in Lists + <Trans>Update {displayName} in Lists</Trans> </Text> <ListsList listsList={listsList} @@ -195,7 +198,7 @@ export const Component = observer(function UserAddRemoveListsImpl({ type="default" onPress={onPressCancel} style={styles.footerBtn} - accessibilityLabel="Cancel" + accessibilityLabel={_(msg`Cancel`)} accessibilityHint="" onAccessibilityEscape={onPressCancel} label="Cancel" @@ -206,7 +209,7 @@ export const Component = observer(function UserAddRemoveListsImpl({ type="primary" onPress={onPressSave} style={styles.footerBtn} - accessibilityLabel="Save changes" + accessibilityLabel={_(msg`Save changes`)} accessibilityHint="" onAccessibilityEscape={onPressSave} label="Save Changes" diff --git a/src/view/com/modals/VerifyEmail.tsx b/src/view/com/modals/VerifyEmail.tsx index 3adaffb14..e48e0e4a2 100644 --- a/src/view/com/modals/VerifyEmail.tsx +++ b/src/view/com/modals/VerifyEmail.tsx @@ -20,6 +20,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {isWeb} from 'platform/detection' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {cleanError} from 'lib/strings/errors' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['90%'] @@ -37,6 +39,7 @@ export const Component = observer(function Component({ }) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const [stage, setStage] = useState<Stages>( showReminder ? Stages.Reminder : Stages.Email, ) @@ -98,21 +101,21 @@ export const Component = observer(function Component({ <Text type="lg" style={[pal.textLight, {marginBottom: 10}]}> {stage === Stages.Reminder ? ( - <> + <Trans> Your email has not yet been verified. This is an important security step which we recommend. - </> + </Trans> ) : stage === Stages.Email ? ( - <> + <Trans> This is important in case you ever need to change your email or reset your password. - </> + </Trans> ) : stage === Stages.ConfirmCode ? ( - <> + <Trans> An email has been sent to{' '} {store.session.currentSession?.email || ''}. It includes a confirmation code which you can enter below. - </> + </Trans> ) : ( '' )} @@ -132,7 +135,7 @@ export const Component = observer(function Component({ </View> <Pressable accessibilityRole="link" - accessibilityLabel="Change my email" + accessibilityLabel={_(msg`Change my email`)} accessibilityHint="" onPress={onEmailIncorrect} style={styles.changeEmailLink}> @@ -150,7 +153,7 @@ export const Component = observer(function Component({ value={confirmationCode} onChangeText={setConfirmationCode} accessible={true} - accessibilityLabel="Confirmation code" + accessibilityLabel={_(msg`Confirmation code`)} accessibilityHint="" autoCapitalize="none" autoComplete="off" @@ -174,7 +177,7 @@ export const Component = observer(function Component({ testID="getStartedBtn" type="primary" onPress={() => setStage(Stages.Email)} - accessibilityLabel="Get Started" + accessibilityLabel={_(msg`Get Started`)} accessibilityHint="" label="Get Started" labelContainerStyle={{justifyContent: 'center', padding: 4}} @@ -187,7 +190,7 @@ export const Component = observer(function Component({ testID="sendEmailBtn" type="primary" onPress={onSendEmail} - accessibilityLabel="Send Confirmation Email" + accessibilityLabel={_(msg`Send Confirmation Email`)} accessibilityHint="" label="Send Confirmation Email" labelContainerStyle={{ @@ -199,7 +202,7 @@ export const Component = observer(function Component({ <Button testID="haveCodeBtn" type="default" - accessibilityLabel="I have a code" + accessibilityLabel={_(msg`I have a code`)} accessibilityHint="" label="I have a confirmation code" labelContainerStyle={{ @@ -216,7 +219,7 @@ export const Component = observer(function Component({ testID="confirmBtn" type="primary" onPress={onConfirm} - accessibilityLabel="Confirm" + accessibilityLabel={_(msg`Confirm`)} accessibilityHint="" label="Confirm" labelContainerStyle={{justifyContent: 'center', padding: 4}} diff --git a/src/view/com/modals/Waitlist.tsx b/src/view/com/modals/Waitlist.tsx index 219bdc583..a31545c0a 100644 --- a/src/view/com/modals/Waitlist.tsx +++ b/src/view/com/modals/Waitlist.tsx @@ -17,6 +17,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' import {ErrorMessage} from '../util/error/ErrorMessage' import {cleanError} from 'lib/strings/errors' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export const snapPoints = ['80%'] @@ -24,6 +26,7 @@ export const snapPoints = ['80%'] export function Component({}: {}) { const pal = usePalette('default') const theme = useTheme() + const {_} = useLingui() const {closeModal} = useModalControls() const [email, setEmail] = React.useState<string>('') const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false) @@ -61,12 +64,14 @@ export function Component({}: {}) { <View style={[styles.container, pal.view]}> <View style={[styles.innerContainer, pal.view]}> <Text type="title-xl" style={[styles.title, pal.text]}> - Join the waitlist + <Trans>Join the waitlist</Trans> </Text> <Text type="lg" style={[styles.description, pal.text]}> - Bluesky uses invites to build a healthier community. If you don't know - anybody with an invite, you can sign up for the waitlist and we'll - send one soon. + <Trans> + Bluesky uses invites to build a healthier community. If you don't + know anybody with an invite, you can sign up for the waitlist and + we'll send one soon. + </Trans> </Text> <TextInput style={[styles.textInput, pal.borderDark, pal.text, s.mb10, s.mt10]} @@ -80,7 +85,7 @@ export function Component({}: {}) { onSubmitEditing={onPressSignup} enterKeyHint="done" accessible={true} - accessibilityLabel="Email" + accessibilityLabel={_(msg`Email`)} accessibilityHint="Input your email to get on the Bluesky waitlist" /> {error ? ( @@ -99,7 +104,9 @@ export function Component({}: {}) { style={pal.text as FontAwesomeIconStyle} /> <Text style={[s.ml10, pal.text]}> - Your email has been saved! We'll be in touch soon. + <Trans> + Your email has been saved! We'll be in touch soon. + </Trans> </Text> </View> ) : ( @@ -114,7 +121,7 @@ export function Component({}: {}) { end={{x: 1, y: 1}} style={[styles.btn]}> <Text type="button-lg" style={[s.white, s.bold]}> - Join Waitlist + <Trans>Join Waitlist</Trans> </Text> </LinearGradient> </TouchableOpacity> @@ -122,11 +129,11 @@ export function Component({}: {}) { style={[styles.btn, s.mt10]} onPress={onCancel} accessibilityRole="button" - accessibilityLabel="Cancel waitlist signup" + accessibilityLabel={_(msg`Cancel waitlist signup`)} accessibilityHint={`Exits signing up for waitlist with ${email}`} onAccessibilityEscape={onCancel}> <Text type="button-lg" style={pal.textLight}> - Cancel + <Trans>Cancel</Trans> </Text> </TouchableOpacity> </> diff --git a/src/view/com/modals/crop-image/CropImage.web.tsx b/src/view/com/modals/crop-image/CropImage.web.tsx index c88d002a9..6f094a1fd 100644 --- a/src/view/com/modals/crop-image/CropImage.web.tsx +++ b/src/view/com/modals/crop-image/CropImage.web.tsx @@ -10,6 +10,8 @@ import {s, gradients} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {SquareIcon, RectWideIcon, RectTallIcon} from 'lib/icons' import {Image as RNImage} from 'react-native-image-crop-picker' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' enum AspectRatio { @@ -35,6 +37,7 @@ export function Component({ }) { const {closeModal} = useModalControls() const pal = usePalette('default') + const {_} = useLingui() const [as, setAs] = React.useState<AspectRatio>(AspectRatio.Square) const [scale, setScale] = React.useState<number>(1) const editorRef = React.useRef<ImageEditor>(null) @@ -96,7 +99,7 @@ export function Component({ <TouchableOpacity onPress={doSetAs(AspectRatio.Wide)} accessibilityRole="button" - accessibilityLabel="Wide" + accessibilityLabel={_(msg`Wide`)} accessibilityHint="Sets image aspect ratio to wide"> <RectWideIcon size={24} @@ -106,7 +109,7 @@ export function Component({ <TouchableOpacity onPress={doSetAs(AspectRatio.Tall)} accessibilityRole="button" - accessibilityLabel="Tall" + accessibilityLabel={_(msg`Tall`)} accessibilityHint="Sets image aspect ratio to tall"> <RectTallIcon size={24} @@ -116,7 +119,7 @@ export function Component({ <TouchableOpacity onPress={doSetAs(AspectRatio.Square)} accessibilityRole="button" - accessibilityLabel="Square" + accessibilityLabel={_(msg`Square`)} accessibilityHint="Sets image aspect ratio to square"> <SquareIcon size={24} @@ -128,7 +131,7 @@ export function Component({ <TouchableOpacity onPress={onPressCancel} accessibilityRole="button" - accessibilityLabel="Cancel image crop" + accessibilityLabel={_(msg`Cancel image crop`)} accessibilityHint="Exits image cropping process"> <Text type="xl" style={pal.link}> Cancel @@ -138,7 +141,7 @@ export function Component({ <TouchableOpacity onPress={onPressDone} accessibilityRole="button" - accessibilityLabel="Save image crop" + accessibilityLabel={_(msg`Save image crop`)} accessibilityHint="Saves image crop settings"> <LinearGradient colors={[gradients.blueLight.start, gradients.blueLight.end]} @@ -146,7 +149,7 @@ export function Component({ end={{x: 1, y: 1}} style={[styles.btn]}> <Text type="xl-medium" style={s.white}> - Done + <Trans>Done</Trans> </Text> </LinearGradient> </TouchableOpacity> diff --git a/src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx b/src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx index c2d0c222a..91e11a19c 100644 --- a/src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx +++ b/src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx @@ -4,6 +4,8 @@ import LinearGradient from 'react-native-linear-gradient' import {s, colors, gradients} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export const ConfirmLanguagesButton = ({ onPress, @@ -13,6 +15,7 @@ export const ConfirmLanguagesButton = ({ extraText?: string }) => { const pal = usePalette('default') + const {_} = useLingui() const {isMobile} = useWebMediaQueries() return ( <View @@ -28,14 +31,16 @@ export const ConfirmLanguagesButton = ({ testID="confirmContentLanguagesBtn" onPress={onPress} accessibilityRole="button" - accessibilityLabel="Confirm content language settings" + accessibilityLabel={_(msg`Confirm content language settings`)} accessibilityHint=""> <LinearGradient colors={[gradients.blueLight.start, gradients.blueLight.end]} start={{x: 0, y: 0}} end={{x: 1, y: 1}} style={[styles.btn]}> - <Text style={[s.white, s.bold, s.f18]}>Done{extraText}</Text> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Done{extraText}</Trans> + </Text> </LinearGradient> </Pressable> </View> diff --git a/src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx b/src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx index 0b19abc68..b8c125b65 100644 --- a/src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx +++ b/src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx @@ -8,6 +8,7 @@ import {deviceLocales} from 'platform/detection' import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages' import {LanguageToggle} from './LanguageToggle' import {ConfirmLanguagesButton} from './ConfirmLanguagesButton' +import {Trans} from '@lingui/macro' import {useModalControls} from '#/state/modals' import { useLanguagePrefs, @@ -69,12 +70,16 @@ export function Component({}: {}) { maxHeight: '90vh', }, ]}> - <Text style={[pal.text, styles.title]}>Content Languages</Text> + <Text style={[pal.text, styles.title]}> + <Trans>Content Languages</Trans> + </Text> <Text style={[pal.text, styles.description]}> - Which languages would you like to see in your algorithmic feeds? + <Trans> + Which languages would you like to see in your algorithmic feeds? + </Trans> </Text> <Text style={[pal.textLight, styles.description]}> - Leave them all unchecked to see any language. + <Trans>Leave them all unchecked to see any language.</Trans> </Text> <ScrollView style={styles.scrollContainer}> {languages.map(lang => ( diff --git a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx index 7ec26ca52..7fcb24d58 100644 --- a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx +++ b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx @@ -9,6 +9,7 @@ import {deviceLocales} from 'platform/detection' import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages' import {ConfirmLanguagesButton} from './ConfirmLanguagesButton' import {ToggleButton} from 'view/com/util/forms/ToggleButton' +import {Trans} from '@lingui/macro' import {useModalControls} from '#/state/modals' import { useLanguagePrefs, @@ -71,9 +72,11 @@ export const Component = observer(function PostLanguagesSettingsImpl() { maxHeight: '90vh', }, ]}> - <Text style={[pal.text, styles.title]}>Post Languages</Text> + <Text style={[pal.text, styles.title]}> + <Trans>Post Languages</Trans> + </Text> <Text style={[pal.text, styles.description]}> - Which languages are used in this post? + <Trans>Which languages are used in this post?</Trans> </Text> <ScrollView style={styles.scrollContainer}> {languages.map(lang => { diff --git a/src/view/com/modals/report/InputIssueDetails.tsx b/src/view/com/modals/report/InputIssueDetails.tsx index 70a8f7b24..2f701b799 100644 --- a/src/view/com/modals/report/InputIssueDetails.tsx +++ b/src/view/com/modals/report/InputIssueDetails.tsx @@ -8,6 +8,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {s} from 'lib/styles' import {SendReportButton} from './SendReportButton' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export function InputIssueDetails({ details, @@ -23,6 +25,7 @@ export function InputIssueDetails({ isProcessing: boolean }) { const pal = usePalette('default') + const {_} = useLingui() const {isMobile} = useWebMediaQueries() return ( @@ -35,14 +38,16 @@ export function InputIssueDetails({ style={[s.mb10, styles.backBtn]} onPress={goBack} accessibilityRole="button" - accessibilityLabel="Add details" + accessibilityLabel={_(msg`Add details`)} accessibilityHint="Add more details to your report"> <FontAwesomeIcon size={18} icon="angle-left" style={[pal.link]} /> - <Text style={[pal.text, s.f18, pal.link]}> Back</Text> + <Text style={[pal.text, s.f18, pal.link]}> + <Trans> Back</Trans> + </Text> </TouchableOpacity> <View style={[pal.btn, styles.detailsInputContainer]}> <TextInput - accessibilityLabel="Text input field" + accessibilityLabel={_(msg`Text input field`)} accessibilityHint="Enter a reason for reporting this post." placeholder="Enter a reason or any other details here." placeholderTextColor={pal.textLight.color} diff --git a/src/view/com/modals/report/Modal.tsx b/src/view/com/modals/report/Modal.tsx index 8dc3f53f7..5183bdc10 100644 --- a/src/view/com/modals/report/Modal.tsx +++ b/src/view/com/modals/report/Modal.tsx @@ -14,6 +14,8 @@ import {SendReportButton} from './SendReportButton' import {InputIssueDetails} from './InputIssueDetails' import {ReportReasonOptions} from './ReasonOptions' import {CollectionId} from './types' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' const DMCA_LINK = 'https://blueskyweb.xyz/support/copyright' @@ -148,6 +150,7 @@ const SelectIssue = ({ atUri: AtUri | null }) => { const pal = usePalette('default') + const {_} = useLingui() const collectionName = getCollectionNameForReport(atUri) const onSelectIssue = (v: string) => setIssue(v) const goToDetails = () => { @@ -160,9 +163,11 @@ const SelectIssue = ({ return ( <> - <Text style={[pal.text, styles.title]}>Report {collectionName}</Text> + <Text style={[pal.text, styles.title]}> + <Trans>Report {collectionName}</Trans> + </Text> <Text style={[pal.textLight, styles.description]}> - What is the issue with this {collectionName}? + <Trans>What is the issue with this {collectionName}?</Trans> </Text> <View style={{marginBottom: 10}}> <ReportReasonOptions @@ -184,9 +189,11 @@ const SelectIssue = ({ style={styles.addDetailsBtn} onPress={goToDetails} accessibilityRole="button" - accessibilityLabel="Add details" + accessibilityLabel={_(msg`Add details`)} accessibilityHint="Add more details to your report"> - <Text style={[s.f18, pal.link]}>Add details to report</Text> + <Text style={[s.f18, pal.link]}> + <Trans>Add details to report</Trans> + </Text> </TouchableOpacity> </> ) : undefined} diff --git a/src/view/com/modals/report/SendReportButton.tsx b/src/view/com/modals/report/SendReportButton.tsx index 82fb65f20..40c239bff 100644 --- a/src/view/com/modals/report/SendReportButton.tsx +++ b/src/view/com/modals/report/SendReportButton.tsx @@ -8,6 +8,8 @@ import { } from 'react-native' import {Text} from '../../util/text/Text' import {s, gradients, colors} from 'lib/styles' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export function SendReportButton({ onPress, @@ -16,6 +18,7 @@ export function SendReportButton({ onPress: () => void isProcessing: boolean }) { + const {_} = useLingui() // loading state // = if (isProcessing) { @@ -31,14 +34,16 @@ export function SendReportButton({ style={s.mt10} onPress={onPress} accessibilityRole="button" - accessibilityLabel="Report post" + accessibilityLabel={_(msg`Report post`)} accessibilityHint={`Reports post with reason and details`}> <LinearGradient colors={[gradients.blueLight.start, gradients.blueLight.end]} start={{x: 0, y: 0}} end={{x: 1, y: 1}} style={[styles.btn]}> - <Text style={[s.white, s.bold, s.f18]}>Send Report</Text> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Send Report</Trans> + </Text> </LinearGradient> </TouchableOpacity> ) diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx index c38ab3fd5..0387ed38d 100644 --- a/src/view/com/notifications/FeedItem.tsx +++ b/src/view/com/notifications/FeedItem.tsx @@ -40,6 +40,8 @@ import {formatCount} from '../util/numeric/format' import {makeProfileLink} from 'lib/routes/links' import {TimeElapsed} from '../util/TimeElapsed' import {isWeb} from 'platform/detection' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' const MAX_AUTHORS = 5 @@ -232,7 +234,9 @@ export const FeedItem = observer(function FeedItemImpl({ /> {authors.length > 1 ? ( <> - <Text style={[pal.text]}> and </Text> + <Text style={[pal.text, s.mr5, s.ml5]}> + <Trans>and</Trans> + </Text> <Text style={[pal.text, s.bold]}> {formatCount(authors.length - 1)}{' '} {pluralize(authors.length - 1, 'other')} @@ -292,6 +296,8 @@ function CondensedAuthorsList({ onToggleAuthorsExpanded: () => void }) { const pal = usePalette('default') + const {_} = useLingui() + if (!visible) { return ( <View style={styles.avis}> @@ -299,7 +305,7 @@ function CondensedAuthorsList({ style={styles.expandedAuthorsCloseBtn} onPress={onToggleAuthorsExpanded} accessibilityRole="button" - accessibilityLabel="Hide user list" + accessibilityLabel={_(msg`Hide user list`)} accessibilityHint="Collapses list of users for a given notification"> <FontAwesomeIcon icon="angle-up" @@ -307,7 +313,7 @@ function CondensedAuthorsList({ style={[styles.expandedAuthorsCloseBtnIcon, pal.text]} /> <Text type="sm-medium" style={pal.text}> - Hide + <Trans>Hide</Trans> </Text> </TouchableOpacity> </View> @@ -328,7 +334,7 @@ function CondensedAuthorsList({ } return ( <TouchableOpacity - accessibilityLabel="Show users" + accessibilityLabel={_(msg`Show users`)} accessibilityHint="Opens an expanded list of users in this notification" onPress={onToggleAuthorsExpanded}> <View style={styles.avis}> diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx index b33829ee5..791fe71be 100644 --- a/src/view/com/pager/FeedsTabBarMobile.tsx +++ b/src/view/com/pager/FeedsTabBarMobile.tsx @@ -14,6 +14,8 @@ import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' import {s} from 'lib/styles' import {HITSLOP_10} from 'lib/constants' import Animated from 'react-native-reanimated' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' import {useSetDrawerOpen} from '#/state/shell/drawer-open' @@ -22,6 +24,7 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl( ) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const setDrawerOpen = useSetDrawerOpen() const items = useHomeTabs(store.preferences.pinnedFeeds) const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3) @@ -45,7 +48,7 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl( testID="viewHeaderDrawerBtn" onPress={onPressAvi} accessibilityRole="button" - accessibilityLabel="Open navigation" + accessibilityLabel={_(msg`Open navigation`)} accessibilityHint="Access profile and other navigation links" hitSlop={HITSLOP_10}> <FontAwesomeIcon @@ -64,7 +67,7 @@ export const FeedsTabBar = observer(function FeedsTabBarImpl( href="/settings/home-feed" hitSlop={HITSLOP_10} accessibilityRole="button" - accessibilityLabel="Home Feed Preferences" + accessibilityLabel={_(msg`Home Feed Preferences`)} accessibilityHint=""> <FontAwesomeIcon icon="sliders" diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 4eb47b0a3..f868c3dca 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -31,6 +31,8 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {NavigationProp} from 'lib/routes/types' import {sanitizeDisplayName} from 'lib/strings/display-names' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' const MAINTAIN_VISIBLE_CONTENT_POSITION = {minIndexForVisible: 2} @@ -79,6 +81,7 @@ export const PostThread = observer(function PostThread({ treeView: boolean }) { const pal = usePalette('default') + const {_} = useLingui() const {isTablet, isDesktop} = useWebMediaQueries() const ref = useRef<FlatList>(null) const hasScrolledIntoView = useRef<boolean>(false) @@ -197,7 +200,7 @@ export const PostThread = observer(function PostThread({ return ( <View style={[pal.border, pal.viewLight, styles.itemContainer]}> <Text type="lg-bold" style={pal.textLight}> - Deleted post. + <Trans>Deleted post.</Trans> </Text> </View> ) @@ -205,7 +208,7 @@ export const PostThread = observer(function PostThread({ return ( <View style={[pal.border, pal.viewLight, styles.itemContainer]}> <Text type="lg-bold" style={pal.textLight}> - Blocked post. + <Trans>Blocked post.</Trans> </Text> </View> ) @@ -214,7 +217,7 @@ export const PostThread = observer(function PostThread({ <Pressable onPress={() => setMaxVisible(n => n + 50)} style={[pal.border, pal.view, styles.itemContainer]} - accessibilityLabel="Load more posts" + accessibilityLabel={_(msg`Load more posts`)} accessibilityHint=""> <View style={[ @@ -222,7 +225,7 @@ export const PostThread = observer(function PostThread({ {paddingHorizontal: 18, paddingVertical: 14, borderRadius: 6}, ]}> <Text type="lg-medium" style={pal.text}> - Load more posts + <Trans>Load more posts</Trans> </Text> </View> </Pressable> @@ -275,6 +278,7 @@ export const PostThread = observer(function PostThread({ posts, onRefresh, treeView, + _, ], ) @@ -302,15 +306,15 @@ export const PostThread = observer(function PostThread({ <CenteredView> <View style={[pal.view, pal.border, styles.notFoundContainer]}> <Text type="title-lg" style={[pal.text, s.mb5]}> - Post not found + <Trans>Post not found</Trans> </Text> <Text type="md" style={[pal.text, s.mb10]}> - The post may have been deleted. + <Trans>The post may have been deleted.</Trans> </Text> <TouchableOpacity onPress={onPressBack} accessibilityRole="button" - accessibilityLabel="Back" + accessibilityLabel={_(msg`Back`)} accessibilityHint=""> <Text type="2xl" style={pal.link}> <FontAwesomeIcon @@ -318,7 +322,7 @@ export const PostThread = observer(function PostThread({ style={[pal.link as FontAwesomeIconStyle, s.mr5]} size={14} /> - Back + <Trans>Back</Trans> </Text> </TouchableOpacity> </View> @@ -336,15 +340,18 @@ export const PostThread = observer(function PostThread({ <CenteredView> <View style={[pal.view, pal.border, styles.notFoundContainer]}> <Text type="title-lg" style={[pal.text, s.mb5]}> - Post hidden + <Trans>Post hidden</Trans> </Text> <Text type="md" style={[pal.text, s.mb10]}> - You have blocked the author or you have been blocked by the author. + <Trans> + You have blocked the author or you have been blocked by the + author. + </Trans> </Text> <TouchableOpacity onPress={onPressBack} accessibilityRole="button" - accessibilityLabel="Back" + accessibilityLabel={_(msg`Back`)} accessibilityHint=""> <Text type="2xl" style={pal.link}> <FontAwesomeIcon @@ -352,7 +359,7 @@ export const PostThread = observer(function PostThread({ style={[pal.link as FontAwesomeIconStyle, s.mr5]} size={14} /> - Back + <Trans>Back</Trans> </Text> </TouchableOpacity> </View> diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index b72121a37..49b769e13 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -37,6 +37,7 @@ import {makeProfileLink} from 'lib/routes/links' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {MAX_POST_LINES} from 'lib/constants' import {logger} from '#/logger' +import {Trans} from '@lingui/macro' import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads' import {useLanguagePrefs} from '#/state/preferences' @@ -176,7 +177,9 @@ export const PostThreadItem = observer(function PostThreadItem({ icon={['far', 'trash-can']} style={pal.icon as FontAwesomeIconStyle} /> - <Text style={[pal.textLight, s.ml10]}>This post has been deleted.</Text> + <Text style={[pal.textLight, s.ml10]}> + <Trans>This post has been deleted.</Trans> + </Text> </View> ) } @@ -650,9 +653,11 @@ function ExpandedPostDetails({ <Text style={pal.textLight}>{niceDate(post.indexedAt)}</Text> {needsTranslation && ( <> - <Text style={pal.textLight}> • </Text> + <Text style={[pal.textLight, s.ml5, s.mr5]}>•</Text> <Link href={translatorUrl} title="Translate"> - <Text style={pal.link}>Translate</Text> + <Text style={pal.link}> + <Trans>Translate</Trans> + </Text> </Link> </> )} diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index 1ee209785..d7b7b8ed7 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -40,6 +40,8 @@ import {makeProfileLink} from 'lib/routes/links' import {Link} from '../util/Link' import {ProfileHeaderSuggestedFollows} from './ProfileHeaderSuggestedFollows' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' interface Props { @@ -114,6 +116,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ const pal = usePalette('default') const palInverted = usePalette('inverted') const store = useStores() + const {_} = useLingui() const {openModal} = useModalControls() const navigation = useNavigation<NavigationProp>() const {track} = useAnalytics() @@ -369,10 +372,10 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ onPress={onPressEditProfile} style={[styles.btn, styles.mainBtn, pal.btn]} accessibilityRole="button" - accessibilityLabel="Edit profile" + accessibilityLabel={_(msg`Edit profile`)} accessibilityHint="Opens editor for profile display name, avatar, background image, and description"> <Text type="button" style={pal.text}> - Edit Profile + <Trans>Edit Profile</Trans> </Text> </TouchableOpacity> ) : view.viewer.blocking ? ( @@ -382,10 +385,10 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ onPress={onPressUnblockAccount} style={[styles.btn, styles.mainBtn, pal.btn]} accessibilityRole="button" - accessibilityLabel="Unblock" + accessibilityLabel={_(msg`Unblock`)} accessibilityHint=""> <Text type="button" style={[pal.text, s.bold]}> - Unblock + <Trans>Unblock</Trans> </Text> </TouchableOpacity> ) @@ -439,7 +442,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ size={14} /> <Text type="button" style={pal.text}> - Following + <Trans>Following</Trans> </Text> </TouchableOpacity> ) : ( @@ -455,7 +458,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ style={[palInverted.text, s.mr5]} /> <Text type="button" style={[palInverted.text, s.bold]}> - Follow + <Trans>Follow</Trans> </Text> </TouchableOpacity> )} @@ -465,7 +468,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ <NativeDropdown testID="profileHeaderDropdownBtn" items={dropdownItems} - accessibilityLabel="More options" + accessibilityLabel={_(msg`More options`)} accessibilityHint=""> <View style={[styles.btn, styles.secondaryBtn, pal.btn]}> <FontAwesomeIcon icon="ellipsis" size={20} style={[pal.text]} /> @@ -488,7 +491,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ {view.viewer.followedBy && !blockHide ? ( <View style={[styles.pill, pal.btn, s.mr5]}> <Text type="xs" style={[pal.text]}> - Follows you + <Trans>Follows you</Trans> </Text> </View> ) : undefined} @@ -533,7 +536,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ {following}{' '} </Text> <Text type="md" style={[pal.textLight]}> - following + <Trans>following</Trans> </Text> </Link> <Text type="md" style={[s.bold, pal.text]}> @@ -572,7 +575,7 @@ const ProfileHeaderLoaded = observer(function ProfileHeaderLoadedImpl({ onPress={onPressBack} hitSlop={BACK_HITSLOP} accessibilityRole="button" - accessibilityLabel="Back" + accessibilityLabel={_(msg`Back`)} accessibilityHint=""> <View style={styles.backBtnWrapper}> <BlurView style={styles.backBtn} blurType="dark"> diff --git a/src/view/com/profile/ProfileSubpageHeader.tsx b/src/view/com/profile/ProfileSubpageHeader.tsx index 0b8015aa9..251d3141b 100644 --- a/src/view/com/profile/ProfileSubpageHeader.tsx +++ b/src/view/com/profile/ProfileSubpageHeader.tsx @@ -17,6 +17,8 @@ import {NavigationProp} from 'lib/routes/types' import {BACK_HITSLOP} from 'lib/constants' import {isNative} from 'platform/detection' import {ImagesLightbox} from 'state/models/ui/shell' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useSetDrawerOpen} from '#/state/shell' export const ProfileSubpageHeader = observer(function HeaderImpl({ @@ -45,6 +47,7 @@ export const ProfileSubpageHeader = observer(function HeaderImpl({ const store = useStores() const setDrawerOpen = useSetDrawerOpen() const navigation = useNavigation<NavigationProp>() + const {_} = useLingui() const {isMobile} = useWebMediaQueries() const pal = usePalette('default') const canGoBack = navigation.canGoBack() @@ -123,7 +126,7 @@ export const ProfileSubpageHeader = observer(function HeaderImpl({ testID="headerAviButton" onPress={onPressAvi} accessibilityRole="image" - accessibilityLabel="View the avatar" + accessibilityLabel={_(msg`View the avatar`)} accessibilityHint="" style={{width: 58}}> <UserAvatar type={avatarType} size={58} avatar={avatar} /> diff --git a/src/view/com/search/HeaderWithInput.tsx b/src/view/com/search/HeaderWithInput.tsx index 1a6b427c6..04cbeab6e 100644 --- a/src/view/com/search/HeaderWithInput.tsx +++ b/src/view/com/search/HeaderWithInput.tsx @@ -11,6 +11,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {useAnalytics} from 'lib/analytics/analytics' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {HITSLOP_10} from 'lib/constants' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useSetDrawerOpen} from '#/state/shell' interface Props { @@ -36,6 +38,7 @@ export function HeaderWithInput({ const setDrawerOpen = useSetDrawerOpen() const theme = useTheme() const pal = usePalette('default') + const {_} = useLingui() const {track} = useAnalytics() const textInput = React.useRef<TextInput>(null) const {isMobile} = useWebMediaQueries() @@ -65,7 +68,7 @@ export function HeaderWithInput({ hitSlop={HITSLOP_10} style={styles.headerMenuBtn} accessibilityRole="button" - accessibilityLabel="Menu" + accessibilityLabel={_(msg`Menu`)} accessibilityHint="Access navigation links and settings"> <FontAwesomeIcon icon="bars" size={18} color={pal.colors.textLight} /> </TouchableOpacity> @@ -95,7 +98,7 @@ export function HeaderWithInput({ onSubmitEditing={onSubmitQuery} autoFocus={false} accessibilityRole="search" - accessibilityLabel="Search" + accessibilityLabel={_(msg`Search`)} accessibilityHint="" autoCorrect={false} autoCapitalize="none" @@ -105,7 +108,7 @@ export function HeaderWithInput({ testID="searchTextInputClearBtn" onPress={onPressClearQuery} accessibilityRole="button" - accessibilityLabel="Clear search query" + accessibilityLabel={_(msg`Clear search query`)} accessibilityHint=""> <FontAwesomeIcon icon="xmark" @@ -120,7 +123,9 @@ export function HeaderWithInput({ <TouchableOpacity onPress={onPressCancelSearchInner} accessibilityRole="button"> - <Text style={pal.text}>Cancel</Text> + <Text style={pal.text}> + <Trans>Cancel</Trans> + </Text> </TouchableOpacity> </View> ) : undefined} diff --git a/src/view/com/util/AccountDropdownBtn.tsx b/src/view/com/util/AccountDropdownBtn.tsx index 29571696b..2042531e9 100644 --- a/src/view/com/util/AccountDropdownBtn.tsx +++ b/src/view/com/util/AccountDropdownBtn.tsx @@ -9,10 +9,14 @@ import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' import {DropdownItem, NativeDropdown} from './forms/NativeDropdown' import * as Toast from '../../com/util/Toast' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' export function AccountDropdownBtn({handle}: {handle: string}) { const store = useStores() const pal = usePalette('default') + const {_} = useLingui() + const items: DropdownItem[] = [ { label: 'Remove account', @@ -34,7 +38,7 @@ export function AccountDropdownBtn({handle}: {handle: string}) { <NativeDropdown testID="accountSettingsDropdownBtn" items={items} - accessibilityLabel="Account options" + accessibilityLabel={_(msg`Account options`)} accessibilityHint=""> <FontAwesomeIcon icon="ellipsis-h" diff --git a/src/view/com/util/BottomSheetCustomBackdrop.tsx b/src/view/com/util/BottomSheetCustomBackdrop.tsx index 91379f1c9..ed5a2f165 100644 --- a/src/view/com/util/BottomSheetCustomBackdrop.tsx +++ b/src/view/com/util/BottomSheetCustomBackdrop.tsx @@ -6,6 +6,7 @@ import Animated, { interpolate, useAnimatedStyle, } from 'react-native-reanimated' +import {t} from '@lingui/macro' export function createCustomBackdrop( onClose?: (() => void) | undefined, @@ -29,7 +30,7 @@ export function createCustomBackdrop( return ( <TouchableWithoutFeedback onPress={onClose} - accessibilityLabel="Close bottom drawer" + accessibilityLabel={t`Close bottom drawer`} accessibilityHint="" onAccessibilityEscape={() => { if (onClose !== undefined) { diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index 9db457325..9bbea1c14 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -16,6 +16,8 @@ import {isWeb, isAndroid} from 'platform/detection' import {Image as RNImage} from 'react-native-image-crop-picker' import {UserPreviewLink} from './UserPreviewLink' import {DropdownItem, NativeDropdown} from './forms/NativeDropdown' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' export type UserAvatarType = 'user' | 'algo' | 'list' @@ -184,6 +186,7 @@ export function EditableUserAvatar({ }: EditableUserAvatarProps) { const store = useStores() const pal = usePalette('default') + const {_} = useLingui() const {requestCameraAccessIfNeeded} = useCameraPermission() const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() @@ -294,7 +297,7 @@ export function EditableUserAvatar({ <NativeDropdown testID="changeAvatarBtn" items={dropdownItems} - accessibilityLabel="Image options" + accessibilityLabel={_(msg`Image options`)} accessibilityHint=""> {avatar ? ( <HighPriorityImage diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx index 4bdfad06c..caae3790b 100644 --- a/src/view/com/util/UserBanner.tsx +++ b/src/view/com/util/UserBanner.tsx @@ -14,6 +14,8 @@ import {usePalette} from 'lib/hooks/usePalette' import {isWeb, isAndroid} from 'platform/detection' import {Image as RNImage} from 'react-native-image-crop-picker' import {NativeDropdown, DropdownItem} from './forms/NativeDropdown' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' export function UserBanner({ banner, @@ -26,6 +28,7 @@ export function UserBanner({ }) { const store = useStores() const pal = usePalette('default') + const {_} = useLingui() const {requestCameraAccessIfNeeded} = useCameraPermission() const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() @@ -112,7 +115,7 @@ export function UserBanner({ <NativeDropdown testID="changeBannerBtn" items={dropdownItems} - accessibilityLabel="Image options" + accessibilityLabel={_(msg`Image options`)} accessibilityHint=""> {banner ? ( <Image diff --git a/src/view/com/util/error/ErrorMessage.tsx b/src/view/com/util/error/ErrorMessage.tsx index 370f10ae3..b4adbb557 100644 --- a/src/view/com/util/error/ErrorMessage.tsx +++ b/src/view/com/util/error/ErrorMessage.tsx @@ -13,6 +13,8 @@ import { import {Text} from '../text/Text' import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' export function ErrorMessage({ message, @@ -27,6 +29,7 @@ export function ErrorMessage({ }) { const theme = useTheme() const pal = usePalette('error') + const {_} = useLingui() return ( <View testID="errorMessageView" style={[styles.outer, pal.view, style]}> <View @@ -49,7 +52,7 @@ export function ErrorMessage({ style={styles.btn} onPress={onPressTryAgain} accessibilityRole="button" - accessibilityLabel="Retry" + accessibilityLabel={_(msg`Retry`)} accessibilityHint="Retries the last action, which errored out"> <FontAwesomeIcon icon="arrows-rotate" diff --git a/src/view/com/util/error/ErrorScreen.tsx b/src/view/com/util/error/ErrorScreen.tsx index a5deeb18f..4cd6dd4b4 100644 --- a/src/view/com/util/error/ErrorScreen.tsx +++ b/src/view/com/util/error/ErrorScreen.tsx @@ -9,6 +9,8 @@ import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' import {Button} from '../forms/Button' import {CenteredView} from '../Views' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export function ErrorScreen({ title, @@ -25,6 +27,8 @@ export function ErrorScreen({ }) { const theme = useTheme() const pal = usePalette('default') + const {_} = useLingui() + return ( <CenteredView testID={testID} style={[styles.outer, pal.view]}> <View style={styles.errorIconContainer}> @@ -58,7 +62,7 @@ export function ErrorScreen({ type="default" style={[styles.btn]} onPress={onPressTryAgain} - accessibilityLabel="Retry" + accessibilityLabel={_(msg`Retry`)} accessibilityHint="Retries the last action, which errored out"> <FontAwesomeIcon icon="arrows-rotate" @@ -66,7 +70,7 @@ export function ErrorScreen({ size={16} /> <Text type="button" style={[styles.btnText, pal.link]}> - Try again + <Trans>Try again</Trans> </Text> </Button> </View> diff --git a/src/view/com/util/forms/DropdownButton.tsx b/src/view/com/util/forms/DropdownButton.tsx index 1bed60b5d..ad8f50f5e 100644 --- a/src/view/com/util/forms/DropdownButton.tsx +++ b/src/view/com/util/forms/DropdownButton.tsx @@ -17,6 +17,8 @@ import {colors} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' import {HITSLOP_10} from 'lib/constants' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' const ESTIMATED_BTN_HEIGHT = 50 const ESTIMATED_SEP_HEIGHT = 16 @@ -207,6 +209,7 @@ const DropdownItems = ({ }: DropDownItemProps) => { const pal = usePalette('default') const theme = useTheme() + const {_} = useLingui() const dropDownBackgroundColor = theme.colorScheme === 'dark' ? pal.btn : pal.view const separatorColor = @@ -224,7 +227,7 @@ const DropdownItems = ({ {/* This TouchableWithoutFeedback renders the background so if the user clicks outside, the dropdown closes */} <TouchableWithoutFeedback onPress={onOuterPress} - accessibilityLabel="Toggle dropdown" + accessibilityLabel={_(msg`Toggle dropdown`)} accessibilityHint=""> <View style={[styles.bg]} /> </TouchableWithoutFeedback> diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index 45abed647..a10841450 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -9,6 +9,8 @@ import { DropdownItem as NativeDropdownItem, } from './NativeDropdown' import {EventStopper} from '../EventStopper' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useModalControls} from '#/state/modals' export function PostDropdownBtn({ @@ -38,6 +40,7 @@ export function PostDropdownBtn({ style?: StyleProp<ViewStyle> }) { const theme = useTheme() + const {_} = useLingui() const defaultCtrlColor = theme.palette.default.postCtrl const {openModal} = useModalControls() @@ -152,7 +155,7 @@ export function PostDropdownBtn({ <NativeDropdown testID={testID} items={dropdownItems} - accessibilityLabel="More post options" + accessibilityLabel={_(msg`More post options`)} accessibilityHint=""> <View style={style}> <FontAwesomeIcon icon="ellipsis" size={20} color={defaultCtrlColor} /> diff --git a/src/view/com/util/forms/SearchInput.tsx b/src/view/com/util/forms/SearchInput.tsx index c1eb82bd4..02b462b55 100644 --- a/src/view/com/util/forms/SearchInput.tsx +++ b/src/view/com/util/forms/SearchInput.tsx @@ -14,6 +14,8 @@ import { import {MagnifyingGlassIcon} from 'lib/icons' import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' interface Props { query: string @@ -33,6 +35,7 @@ export function SearchInput({ }: Props) { const theme = useTheme() const pal = usePalette('default') + const {_} = useLingui() const textInput = React.useRef<TextInput>(null) const onPressCancelSearchInner = React.useCallback(() => { @@ -58,7 +61,7 @@ export function SearchInput({ onChangeText={onChangeQuery} onSubmitEditing={onSubmitQuery} accessibilityRole="search" - accessibilityLabel="Search" + accessibilityLabel={_(msg`Search`)} accessibilityHint="" autoCorrect={false} autoCapitalize="none" @@ -67,7 +70,7 @@ export function SearchInput({ <TouchableOpacity onPress={onPressCancelSearchInner} accessibilityRole="button" - accessibilityLabel="Clear search query" + accessibilityLabel={_(msg`Clear search query`)} accessibilityHint=""> <FontAwesomeIcon icon="xmark" diff --git a/src/view/com/util/moderation/ContentHider.tsx b/src/view/com/util/moderation/ContentHider.tsx index b6fe0dd8c..a13aae2b5 100644 --- a/src/view/com/util/moderation/ContentHider.tsx +++ b/src/view/com/util/moderation/ContentHider.tsx @@ -6,6 +6,8 @@ import {ModerationUI} from '@atproto/api' import {Text} from '../text/Text' import {ShieldExclamation} from 'lib/icons' import {describeModerationCause} from 'lib/moderation' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useModalControls} from '#/state/modals' export function ContentHider({ @@ -23,6 +25,7 @@ export function ContentHider({ childContainerStyle?: StyleProp<ViewStyle> }>) { const pal = usePalette('default') + const {_} = useLingui() const {isMobile} = useWebMediaQueries() const [override, setOverride] = React.useState(false) const {openModal} = useModalControls() @@ -69,7 +72,7 @@ export function ContentHider({ }) }} accessibilityRole="button" - accessibilityLabel="Learn more about this warning" + accessibilityLabel={_(msg`Learn more about this warning`)} accessibilityHint=""> <ShieldExclamation size={18} style={pal.text} /> </Pressable> diff --git a/src/view/com/util/moderation/PostAlerts.tsx b/src/view/com/util/moderation/PostAlerts.tsx index 2c9a71859..bc5bf9b32 100644 --- a/src/view/com/util/moderation/PostAlerts.tsx +++ b/src/view/com/util/moderation/PostAlerts.tsx @@ -5,6 +5,8 @@ import {Text} from '../text/Text' import {usePalette} from 'lib/hooks/usePalette' import {ShieldExclamation} from 'lib/icons' import {describeModerationCause} from 'lib/moderation' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export function PostAlerts({ @@ -16,6 +18,7 @@ export function PostAlerts({ style?: StyleProp<ViewStyle> }) { const pal = usePalette('default') + const {_} = useLingui() const {openModal} = useModalControls() const shouldAlert = !!moderation.cause && moderation.alert @@ -34,14 +37,14 @@ export function PostAlerts({ }) }} accessibilityRole="button" - accessibilityLabel="Learn more about this warning" + accessibilityLabel={_(msg`Learn more about this warning`)} accessibilityHint="" style={[styles.container, pal.viewLight, style]}> <ShieldExclamation style={pal.text} size={16} /> <Text type="lg" style={[pal.text]}> {desc.name}{' '} <Text type="lg" style={[pal.link, styles.learnMoreBtn]}> - Learn More + <Trans>Learn More</Trans> </Text> </Text> </Pressable> diff --git a/src/view/com/util/moderation/PostHider.tsx b/src/view/com/util/moderation/PostHider.tsx index a9ccf2ebd..c2b857f54 100644 --- a/src/view/com/util/moderation/PostHider.tsx +++ b/src/view/com/util/moderation/PostHider.tsx @@ -8,6 +8,8 @@ import {Text} from '../text/Text' import {addStyle} from 'lib/styles' import {describeModerationCause} from 'lib/moderation' import {ShieldExclamation} from 'lib/icons' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useModalControls} from '#/state/modals' interface Props extends ComponentProps<typeof Link> { @@ -26,6 +28,7 @@ export function PostHider({ ...props }: Props) { const pal = usePalette('default') + const {_} = useLingui() const {isMobile} = useWebMediaQueries() const [override, setOverride] = React.useState(false) const {openModal} = useModalControls() @@ -70,7 +73,7 @@ export function PostHider({ }) }} accessibilityRole="button" - accessibilityLabel="Learn more about this warning" + accessibilityLabel={_(msg`Learn more about this warning`)} accessibilityHint=""> <ShieldExclamation size={18} style={pal.text} /> </Pressable> diff --git a/src/view/com/util/moderation/ProfileHeaderAlerts.tsx b/src/view/com/util/moderation/ProfileHeaderAlerts.tsx index d2406e7ae..d2675ca54 100644 --- a/src/view/com/util/moderation/ProfileHeaderAlerts.tsx +++ b/src/view/com/util/moderation/ProfileHeaderAlerts.tsx @@ -8,6 +8,8 @@ import { describeModerationCause, getProfileModerationCauses, } from 'lib/moderation' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' export function ProfileHeaderAlerts({ @@ -18,6 +20,7 @@ export function ProfileHeaderAlerts({ style?: StyleProp<ViewStyle> }) { const pal = usePalette('default') + const {_} = useLingui() const {openModal} = useModalControls() const causes = getProfileModerationCauses(moderation) @@ -41,7 +44,7 @@ export function ProfileHeaderAlerts({ }) }} accessibilityRole="button" - accessibilityLabel="Learn more about this warning" + accessibilityLabel={_(msg`Learn more about this warning`)} accessibilityHint="" style={[styles.container, pal.viewLight, style]}> <ShieldExclamation style={pal.text} size={24} /> @@ -49,7 +52,7 @@ export function ProfileHeaderAlerts({ {desc.name} </Text> <Text type="lg" style={[pal.link, styles.learnMoreBtn]}> - Learn More + <Trans>Learn More</Trans> </Text> </Pressable> ) diff --git a/src/view/com/util/moderation/ScreenHider.tsx b/src/view/com/util/moderation/ScreenHider.tsx index c3d23b84d..946f937e9 100644 --- a/src/view/com/util/moderation/ScreenHider.tsx +++ b/src/view/com/util/moderation/ScreenHider.tsx @@ -18,7 +18,10 @@ import {NavigationProp} from 'lib/routes/types' import {Text} from '../text/Text' import {Button} from '../forms/Button' import {describeModerationCause} from 'lib/moderation' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' +import {s} from '#/lib/styles' export function ScreenHider({ testID, @@ -36,6 +39,7 @@ export function ScreenHider({ }>) { const pal = usePalette('default') const palInverted = usePalette('inverted') + const {_} = useLingui() const [override, setOverride] = React.useState(false) const navigation = useNavigation<NavigationProp>() const {isMobile} = useWebMediaQueries() @@ -62,14 +66,13 @@ export function ScreenHider({ </View> </View> <Text type="title-2xl" style={[styles.title, pal.text]}> - Content Warning + <Trans>Content Warning</Trans> </Text> <Text type="2xl" style={[styles.description, pal.textLight]}> - This {screenDescription} has been flagged:{' '} - <Text type="2xl-medium" style={pal.text}> - {desc.name} + <Trans>This {screenDescription} has been flagged:</Trans> + <Text type="2xl-medium" style={[pal.text, s.ml5]}> + {desc.name}. </Text> - .{' '} <TouchableWithoutFeedback onPress={() => { openModal({ @@ -79,10 +82,10 @@ export function ScreenHider({ }) }} accessibilityRole="button" - accessibilityLabel="Learn more about this warning" + accessibilityLabel={_(msg`Learn more about this warning`)} accessibilityHint=""> <Text type="2xl" style={pal.link}> - Learn More + <Trans>Learn More</Trans> </Text> </TouchableWithoutFeedback> </Text> @@ -99,7 +102,7 @@ export function ScreenHider({ }} style={styles.btn}> <Text type="button-lg" style={pal.textInverted}> - Go back + <Trans>Go back</Trans> </Text> </Button> {!moderation.noOverride && ( @@ -108,7 +111,7 @@ export function ScreenHider({ onPress={() => setOverride(v => !v)} style={styles.btn}> <Text type="button-lg" style={pal.text}> - Show anyway + <Trans>Show anyway</Trans> </Text> </Button> )} diff --git a/src/view/com/util/post-ctrls/RepostButton.web.tsx b/src/view/com/util/post-ctrls/RepostButton.web.tsx index 57f544d41..70f7229d4 100644 --- a/src/view/com/util/post-ctrls/RepostButton.web.tsx +++ b/src/view/com/util/post-ctrls/RepostButton.web.tsx @@ -10,6 +10,8 @@ import { DropdownItem as NativeDropdownItem, } from '../forms/NativeDropdown' import {EventStopper} from '../EventStopper' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' interface Props { isReposted: boolean @@ -28,6 +30,7 @@ export const RepostButton = ({ onQuote, }: Props) => { const theme = useTheme() + const {_} = useLingui() const defaultControlColor = React.useMemo( () => ({ @@ -63,7 +66,7 @@ export const RepostButton = ({ <EventStopper> <NativeDropdown items={dropdownItems} - accessibilityLabel="Repost or quote post" + accessibilityLabel={_(msg`Repost or quote post`)} accessibilityHint=""> <View style={[ diff --git a/src/view/screens/AppPasswords.tsx b/src/view/screens/AppPasswords.tsx index 338adfbaf..092f4bc2c 100644 --- a/src/view/screens/AppPasswords.tsx +++ b/src/view/screens/AppPasswords.tsx @@ -16,6 +16,8 @@ import {useAnalytics} from 'lib/analytics/analytics' import {useFocusEffect} from '@react-navigation/native' import {ViewHeader} from '../com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useSetMinimalShellMode} from '#/state/shell' import {useModalControls} from '#/state/modals' import {useLanguagePrefs} from '#/state/preferences' @@ -55,8 +57,10 @@ export const AppPasswords = withAuthRequired( <AppPasswordsHeader /> <View style={[styles.empty, pal.viewLight]}> <Text type="lg" style={[pal.text, styles.emptyText]}> - You have not created any app passwords yet. You can create one by - pressing the button below. + <Trans> + You have not created any app passwords yet. You can create one + by pressing the button below. + </Trans> </Text> </View> {!isTabletOrDesktop && <View style={styles.flex1} />} @@ -146,8 +150,10 @@ function AppPasswordsHeader() { pal.text, isTabletOrDesktop && styles.descriptionDesktop, ]}> - Use app passwords to login to other Bluesky clients without giving full - access to your account or password. + <Trans> + Use app passwords to login to other Bluesky clients without giving + full access to your account or password. + </Trans> </Text> </> ) @@ -164,6 +170,7 @@ function AppPassword({ }) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const {openModal} = useModalControls() const {contentLanguages} = useLanguagePrefs() @@ -188,7 +195,7 @@ function AppPassword({ style={[styles.item, pal.border]} onPress={onDelete} accessibilityRole="button" - accessibilityLabel="Delete app password" + accessibilityLabel={_(msg`Delete app password`)} accessibilityHint=""> <View> <Text type="md-bold" style={pal.text}> diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index 169660a8f..c2ec9208f 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -27,6 +27,8 @@ import {FeedSourceModel} from 'state/models/content/feed-source' import {FlatList} from 'view/com/util/Views' import {useFocusEffect} from '@react-navigation/native' import {FeedSourceCard} from 'view/com/feeds/FeedSourceCard' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useSetMinimalShellMode} from '#/state/shell' type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'> @@ -34,6 +36,7 @@ export const FeedsScreen = withAuthRequired( observer<Props>(function FeedsScreenImpl({}: Props) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const {isMobile, isTabletOrDesktop} = useWebMediaQueries() const myFeeds = store.me.myFeeds @@ -88,12 +91,12 @@ export const FeedsScreen = withAuthRequired( href="/settings/saved-feeds" hitSlop={10} accessibilityRole="button" - accessibilityLabel="Edit Saved Feeds" + accessibilityLabel={_(msg`Edit Saved Feeds`)} accessibilityHint="Opens screen to edit Saved Feeds"> <CogIcon size={22} strokeWidth={2} style={pal.textLight} /> </Link> ) - }, [pal]) + }, [pal, _]) const onRefresh = React.useCallback(() => { myFeeds.refresh() @@ -124,11 +127,11 @@ export const FeedsScreen = withAuthRequired( }, ]}> <Text type="title-lg" style={[pal.text, s.bold]}> - My Feeds + <Trans>My Feeds</Trans> </Text> <Link href="/settings/saved-feeds" - accessibilityLabel="Edit My Feeds" + accessibilityLabel={_(msg`Edit My Feeds`)} accessibilityHint=""> <CogIcon strokeWidth={1.5} style={pal.icon} size={28} /> </Link> @@ -139,7 +142,7 @@ export const FeedsScreen = withAuthRequired( } else if (item.type === 'saved-feeds-loading') { return ( <> - {Array.from(Array(item.numItems)).map((_, i) => ( + {Array.from(Array(item.numItems)).map((_i, i) => ( <SavedFeedLoadingPlaceholder key={`placeholder-${i}`} /> ))} </> @@ -161,7 +164,7 @@ export const FeedsScreen = withAuthRequired( }, ]}> <Text type="title-lg" style={[pal.text, s.bold]}> - Discover new feeds + <Trans>Discover new feeds</Trans> </Text> {!isMobile && ( <SearchInput @@ -203,14 +206,22 @@ export const FeedsScreen = withAuthRequired( paddingBottom: '150%', }}> <Text type="lg" style={pal.textLight}> - No results found for "{query}" + <Trans>No results found for "{query}"</Trans> </Text> </View> ) } return null }, - [isMobile, pal, query, onChangeQuery, onPressCancelSearch, onSubmitQuery], + [ + isMobile, + pal, + query, + onChangeQuery, + onPressCancelSearch, + onSubmitQuery, + _, + ], ) return ( @@ -249,7 +260,7 @@ export const FeedsScreen = withAuthRequired( onPress={onPressCompose} icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />} accessibilityRole="button" - accessibilityLabel="New post" + accessibilityLabel={_(msg`New post`)} accessibilityHint="" /> </View> @@ -289,7 +300,7 @@ function SavedFeed({feed}: {feed: FeedSourceModel}) { {feed.error ? ( <View style={[styles.offlineSlug, pal.borderDark]}> <Text type="xs" style={pal.textLight}> - Feed offline + <Trans>Feed offline</Trans> </Text> </View> ) : null} diff --git a/src/view/screens/Log.tsx b/src/view/screens/Log.tsx index f524279a5..69c07edae 100644 --- a/src/view/screens/Log.tsx +++ b/src/view/screens/Log.tsx @@ -11,6 +11,8 @@ import {Text} from '../com/util/text/Text' import {usePalette} from 'lib/hooks/usePalette' import {getEntries} from '#/logger/logDump' import {ago} from 'lib/strings/time' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useSetMinimalShellMode} from '#/state/shell' export const LogScreen = observer(function Log({}: NativeStackScreenProps< @@ -18,6 +20,7 @@ export const LogScreen = observer(function Log({}: NativeStackScreenProps< 'Log' >) { const pal = usePalette('default') + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const [expanded, setExpanded] = React.useState<string[]>([]) @@ -47,7 +50,7 @@ export const LogScreen = observer(function Log({}: NativeStackScreenProps< <TouchableOpacity style={[styles.entry, pal.border, pal.view]} onPress={toggler(entry.id)} - accessibilityLabel="View debug entry" + accessibilityLabel={_(msg`View debug entry`)} accessibilityHint="Opens additional details for a debug entry"> {entry.level === 'debug' ? ( <FontAwesomeIcon icon="info" /> diff --git a/src/view/screens/PreferencesHomeFeed.tsx b/src/view/screens/PreferencesHomeFeed.tsx index 21c15931f..da99dc16f 100644 --- a/src/view/screens/PreferencesHomeFeed.tsx +++ b/src/view/screens/PreferencesHomeFeed.tsx @@ -14,6 +14,8 @@ import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' import {ViewHeader} from 'view/com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' import debounce from 'lodash.debounce' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' function RepliesThresholdInput({enabled}: {enabled: boolean}) { const store = useStores() @@ -66,6 +68,7 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ }: Props) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const {isTabletOrDesktop} = useWebMediaQueries() return ( @@ -84,7 +87,7 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ isTabletOrDesktop && {paddingTop: 20, paddingBottom: 20}, ]}> <Text type="xl" style={[pal.textLight, styles.description]}> - Fine-tune the content you see on your home screen. + <Trans>Fine-tune the content you see on your home screen.</Trans> </Text> </View> @@ -92,10 +95,12 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ <View style={styles.cardsContainer}> <View style={[pal.viewLight, styles.card]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Show Replies + <Trans>Show Replies</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Set this setting to "No" to hide all replies from your feed. + <Trans> + Set this setting to "No" to hide all replies from your feed. + </Trans> </Text> <ToggleButton testID="toggleRepliesBtn" @@ -112,10 +117,13 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ store.preferences.homeFeed.hideReplies && styles.dimmed, ]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Reply Filters + <Trans>Reply Filters</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Enable this setting to only see replies between people you follow. + <Trans> + Enable this setting to only see replies between people you + follow. + </Trans> </Text> <ToggleButton type="default-light" @@ -129,8 +137,10 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ style={[s.mb10]} /> <Text style={[pal.text]}> - Adjust the number of likes a reply must have to be shown in your - feed. + <Trans> + Adjust the number of likes a reply must have to be shown in your + feed. + </Trans> </Text> <RepliesThresholdInput enabled={!store.preferences.homeFeed.hideReplies} @@ -139,10 +149,12 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ <View style={[pal.viewLight, styles.card]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Show Reposts + <Trans>Show Reposts</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Set this setting to "No" to hide all reposts from your feed. + <Trans> + Set this setting to "No" to hide all reposts from your feed. + </Trans> </Text> <ToggleButton type="default-light" @@ -154,11 +166,13 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ <View style={[pal.viewLight, styles.card]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Show Quote Posts + <Trans>Show Quote Posts</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Set this setting to "No" to hide all quote posts from your feed. - Reposts will still be visible. + <Trans> + Set this setting to "No" to hide all quote posts from your feed. + Reposts will still be visible. + </Trans> </Text> <ToggleButton type="default-light" @@ -170,12 +184,14 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ <View style={[pal.viewLight, styles.card]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - <FontAwesomeIcon icon="flask" color={pal.colors.text} /> Show - Posts from My Feeds + <FontAwesomeIcon icon="flask" color={pal.colors.text} /> + <Trans>Show Posts from My Feeds</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Set this setting to "Yes" to show samples of your saved feeds in - your following feed. This is an experimental feature. + <Trans> + Set this setting to "Yes" to show samples of your saved feeds in + your following feed. This is an experimental feature. + </Trans> </Text> <ToggleButton type="default-light" @@ -204,9 +220,11 @@ export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({ }} style={[styles.btn, isTabletOrDesktop && styles.btnDesktop]} accessibilityRole="button" - accessibilityLabel="Confirm" + accessibilityLabel={_(msg`Confirm`)} accessibilityHint=""> - <Text style={[s.white, s.bold, s.f18]}>Done</Text> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Done</Trans> + </Text> </TouchableOpacity> </View> </CenteredView> diff --git a/src/view/screens/PreferencesThreads.tsx b/src/view/screens/PreferencesThreads.tsx index af98a1833..8a2db13ce 100644 --- a/src/view/screens/PreferencesThreads.tsx +++ b/src/view/screens/PreferencesThreads.tsx @@ -12,6 +12,8 @@ import {RadioGroup} from 'view/com/util/forms/RadioGroup' import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' import {ViewHeader} from 'view/com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'PreferencesThreads'> export const PreferencesThreads = observer(function PreferencesThreadsImpl({ @@ -19,6 +21,7 @@ export const PreferencesThreads = observer(function PreferencesThreadsImpl({ }: Props) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const {isTabletOrDesktop} = useWebMediaQueries() return ( @@ -37,7 +40,7 @@ export const PreferencesThreads = observer(function PreferencesThreadsImpl({ isTabletOrDesktop && {paddingTop: 20, paddingBottom: 20}, ]}> <Text type="xl" style={[pal.textLight, styles.description]}> - Fine-tune the discussion threads. + <Trans>Fine-tune the discussion threads.</Trans> </Text> </View> @@ -45,10 +48,10 @@ export const PreferencesThreads = observer(function PreferencesThreadsImpl({ <View style={styles.cardsContainer}> <View style={[pal.viewLight, styles.card]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Sort Replies + <Trans>Sort Replies</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Sort replies to the same post by: + <Trans>Sort replies to the same post by:</Trans> </Text> <View style={[pal.view, {borderRadius: 8, paddingVertical: 6}]}> <RadioGroup @@ -67,10 +70,12 @@ export const PreferencesThreads = observer(function PreferencesThreadsImpl({ <View style={[pal.viewLight, styles.card]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - Prioritize Your Follows + <Trans>Prioritize Your Follows</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Show replies by people you follow before all other replies. + <Trans> + Show replies by people you follow before all other replies. + </Trans> </Text> <ToggleButton type="default-light" @@ -84,12 +89,14 @@ export const PreferencesThreads = observer(function PreferencesThreadsImpl({ <View style={[pal.viewLight, styles.card]}> <Text type="title-sm" style={[pal.text, s.pb5]}> - <FontAwesomeIcon icon="flask" color={pal.colors.text} /> Threaded - Mode + <FontAwesomeIcon icon="flask" color={pal.colors.text} />{' '} + <Trans>Threaded Mode</Trans> </Text> <Text style={[pal.text, s.pb10]}> - Set this setting to "Yes" to show replies in a threaded view. This - is an experimental feature. + <Trans> + Set this setting to "Yes" to show replies in a threaded view. + This is an experimental feature. + </Trans> </Text> <ToggleButton type="default-light" @@ -118,9 +125,11 @@ export const PreferencesThreads = observer(function PreferencesThreadsImpl({ }} style={[styles.btn, isTabletOrDesktop && styles.btnDesktop]} accessibilityRole="button" - accessibilityLabel="Confirm" + accessibilityLabel={_(msg`Confirm`)} accessibilityHint=""> - <Text style={[s.white, s.bold, s.f18]}>Done</Text> + <Text style={[s.white, s.bold, s.f18]}> + <Trans>Done</Trans> + </Text> </TouchableOpacity> </View> </CenteredView> diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 9a25612ad..f2aa7f05d 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -30,6 +30,8 @@ import {FeedSourceModel} from 'state/models/content/feed-source' import {useSetTitle} from 'lib/hooks/useSetTitle' import {combinedDisplayName} from 'lib/strings/display-names' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useSetMinimalShellMode} from '#/state/shell' type Props = NativeStackScreenProps<CommonNavigatorParams, 'Profile'> @@ -38,6 +40,7 @@ export const ProfileScreen = withAuthRequired( const store = useStores() const setMinimalShellMode = useSetMinimalShellMode() const {screen, track} = useAnalytics() + const {_} = useLingui() const viewSelectorRef = React.useRef<ViewSelectorHandle>(null) const name = route.params.name === 'me' ? store.me.did : route.params.name @@ -206,7 +209,11 @@ export const ProfileScreen = withAuthRequired( // if section is posts or posts & replies } else { if (item === ProfileUiModel.END_ITEM) { - return <Text style={styles.endItem}>- end of feed -</Text> + return ( + <Text style={styles.endItem}> + <Trans>- end of feed -</Trans> + </Text> + ) } else if (item === ProfileUiModel.LOADING_ITEM) { return <PostFeedLoadingPlaceholder /> } else if (item._reactKey === '__error__') { @@ -296,7 +303,7 @@ export const ProfileScreen = withAuthRequired( onPress={onPressCompose} icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />} accessibilityRole="button" - accessibilityLabel="New post" + accessibilityLabel={_(msg`New post`)} accessibilityHint="" /> </ScreenHider> diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx index 3d1081642..8a8851c97 100644 --- a/src/view/screens/ProfileFeed.tsx +++ b/src/view/screens/ProfileFeed.tsx @@ -47,6 +47,8 @@ import {sanitizeHandle} from 'lib/strings/handles' import {makeProfileLink} from 'lib/routes/links' import {ComposeIcon2} from 'lib/icons' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useModalControls} from '#/state/modals' const SECTION_TITLES = ['Posts', 'About'] @@ -60,6 +62,7 @@ export const ProfileFeedScreen = withAuthRequired( observer(function ProfileFeedScreenImpl(props: Props) { const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const navigation = useNavigation<NavigationProp>() const {name: handleOrDid} = props.route.params @@ -98,7 +101,7 @@ export const ProfileFeedScreen = withAuthRequired( <CenteredView> <View style={[pal.view, pal.border, styles.notFoundContainer]}> <Text type="title-lg" style={[pal.text, s.mb10]}> - Could not load feed + <Trans>Could not load feed</Trans> </Text> <Text type="md" style={[pal.text, s.mb20]}> {error} @@ -107,12 +110,12 @@ export const ProfileFeedScreen = withAuthRequired( <View style={{flexDirection: 'row'}}> <Button type="default" - accessibilityLabel="Go Back" + accessibilityLabel={_(msg`Go Back`)} accessibilityHint="Return to previous page" onPress={onPressBack} style={{flexShrink: 1}}> <Text type="button" style={pal.text}> - Go Back + <Trans>Go Back</Trans> </Text> </Button> </View> @@ -142,6 +145,7 @@ export const ProfileFeedScreenInner = observer( const pal = usePalette('default') const store = useStores() const {track} = useAnalytics() + const {_} = useLingui() const feedSectionRef = React.useRef<SectionRef>(null) const {rkey, name: handleOrDid} = route.params const uri = useMemo( @@ -313,7 +317,7 @@ export const ProfileFeedScreenInner = observer( <NativeDropdown testID="headerDropdownBtn" items={dropdownItems} - accessibilityLabel="More options" + accessibilityLabel={_(msg`More options`)} accessibilityHint=""> <View style={[pal.viewLight, styles.btn]}> <FontAwesomeIcon @@ -334,6 +338,7 @@ export const ProfileFeedScreenInner = observer( onTogglePinned, onToggleSaved, dropdownItems, + _, ]) return ( @@ -374,7 +379,7 @@ export const ProfileFeedScreenInner = observer( /> } accessibilityRole="button" - accessibilityLabel="New post" + accessibilityLabel={_(msg`New post`)} accessibilityHint="" /> </View> @@ -448,6 +453,7 @@ const AboutSection = observer(function AboutPageImpl({ onScroll: (e: NativeScrollEvent) => void }) { const pal = usePalette('default') + const {_} = useLingui() const scrollHandler = useAnimatedScrollHandler({onScroll}) if (!feedInfo) { @@ -478,14 +484,14 @@ const AboutSection = observer(function AboutPageImpl({ /> ) : ( <Text type="lg" style={[{fontStyle: 'italic'}, pal.textLight]}> - No description + <Trans>No description</Trans> </Text> )} <View style={{flexDirection: 'row', alignItems: 'center', gap: 10}}> <Button type="default" testID="toggleLikeBtn" - accessibilityLabel="Like this feed" + accessibilityLabel={_(msg`Like this feed`)} accessibilityHint="" onPress={onToggleLiked} style={{paddingHorizontal: 10}}> diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index a165502b7..0bbb512c1 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -45,6 +45,8 @@ import {makeProfileLink, makeListLink} from 'lib/routes/links' import {ComposeIcon2} from 'lib/icons' import {ListItems} from 'view/com/lists/ListItems' import {logger} from '#/logger' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useSetMinimalShellMode} from '#/state/shell' import {useModalControls} from '#/state/modals' @@ -107,6 +109,7 @@ export const ProfileListScreenInner = observer( listOwnerDid, }: Props & {listOwnerDid: string}) { const store = useStores() + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const {rkey} = route.params const feedSectionRef = React.useRef<SectionRef>(null) @@ -208,7 +211,7 @@ export const ProfileListScreenInner = observer( /> } accessibilityRole="button" - accessibilityLabel="New post" + accessibilityLabel={_(msg`New post`)} accessibilityHint="" /> </View> @@ -246,7 +249,7 @@ export const ProfileListScreenInner = observer( /> } accessibilityRole="button" - accessibilityLabel="New post" + accessibilityLabel={_(msg`New post`)} accessibilityHint="" /> </View> @@ -270,6 +273,7 @@ const Header = observer(function HeaderImpl({ }) { const pal = usePalette('default') const palInverted = usePalette('inverted') + const {_} = useLingui() const navigation = useNavigation<NavigationProp>() const {openModal, closeModal} = useModalControls() @@ -526,10 +530,12 @@ const Header = observer(function HeaderImpl({ <NativeDropdown testID="subscribeBtn" items={subscribeDropdownItems} - accessibilityLabel="Subscribe to this list" + accessibilityLabel={_(msg`Subscribe to this list`)} accessibilityHint=""> <View style={[palInverted.view, styles.btn]}> - <Text style={palInverted.text}>Subscribe</Text> + <Text style={palInverted.text}> + <Trans>Subscribe</Trans> + </Text> </View> </NativeDropdown> ) @@ -537,7 +543,7 @@ const Header = observer(function HeaderImpl({ <NativeDropdown testID="headerDropdownBtn" items={dropdownItems} - accessibilityLabel="More options" + accessibilityLabel={_(msg`More options`)} accessibilityHint=""> <View style={[pal.viewLight, styles.btn]}> <FontAwesomeIcon icon="ellipsis" size={20} color={pal.colors.text} /> @@ -624,6 +630,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( ref, ) { const pal = usePalette('default') + const {_} = useLingui() const {isMobile} = useWebMediaQueries() const scrollElRef = React.useRef<FlatList>(null) @@ -662,7 +669,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( testID="listDescriptionEmpty" type="lg" style={[{fontStyle: 'italic'}, pal.textLight]}> - No description + <Trans>No description</Trans> </Text> )} <Text type="md" style={[pal.textLight]} numberOfLines={1}> @@ -688,12 +695,14 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( paddingBottom: isMobile ? 14 : 18, }, ]}> - <Text type="lg-bold">Users</Text> + <Text type="lg-bold"> + <Trans>Users</Trans> + </Text> {isOwner && ( <Pressable testID="addUserBtn" accessibilityRole="button" - accessibilityLabel="Add a user to this list" + accessibilityLabel={_(msg`Add a user to this list`)} accessibilityHint="" onPress={onPressAddUser} style={{flexDirection: 'row', alignItems: 'center', gap: 6}}> @@ -702,7 +711,9 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( color={pal.colors.link} size={16} /> - <Text style={pal.link}>Add</Text> + <Text style={pal.link}> + <Trans>Add</Trans> + </Text> </Pressable> )} </View> @@ -717,6 +728,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( isCurateList, isOwner, onPressAddUser, + _, ]) const renderEmptyState = useCallback(() => { @@ -757,6 +769,7 @@ const AboutSection = React.forwardRef<SectionRef, AboutSectionProps>( function ErrorScreen({error}: {error: string}) { const pal = usePalette('default') const navigation = useNavigation<NavigationProp>() + const {_} = useLingui() const onPressBack = useCallback(() => { if (navigation.canGoBack()) { navigation.goBack() @@ -778,7 +791,7 @@ function ErrorScreen({error}: {error: string}) { }, ]}> <Text type="title-lg" style={[pal.text, s.mb10]}> - Could not load list + <Trans>Could not load list</Trans> </Text> <Text type="md" style={[pal.text, s.mb20]}> {error} @@ -787,12 +800,12 @@ function ErrorScreen({error}: {error: string}) { <View style={{flexDirection: 'row'}}> <Button type="default" - accessibilityLabel="Go Back" + accessibilityLabel={_(msg`Go Back`)} accessibilityHint="Return to previous page" onPress={onPressBack} style={{flexShrink: 1}}> <Text type="button" style={pal.text}> - Go Back + <Trans>Go Back</Trans> </Text> </Button> </View> diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index f912996ef..010de23db 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -63,6 +63,8 @@ import { // -prf import {useDebugHeaderSetting} from 'lib/api/debug-appview-proxy-header' import {STATUS_PAGE_URL} from 'lib/constants' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'> export const SettingsScreen = withAuthRequired( @@ -71,6 +73,7 @@ export const SettingsScreen = withAuthRequired( const setColorMode = useSetColorMode() const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const setMinimalShellMode = useSetMinimalShellMode() const requireAltTextEnabled = useRequireAltTextEnabled() const setRequireAltTextEnabled = useSetRequireAltTextEnabled() @@ -213,7 +216,7 @@ export const SettingsScreen = withAuthRequired( {store.session.currentSession !== undefined ? ( <> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Account + <Trans>Account</Trans> </Text> <View style={[styles.infoLine]}> <Text type="lg-medium" style={pal.text}> @@ -233,17 +236,17 @@ export const SettingsScreen = withAuthRequired( </Text> <Link onPress={() => openModal({name: 'change-email'})}> <Text type="lg" style={pal.link}> - Change + <Trans>Change</Trans> </Text> </Link> </View> <View style={[styles.infoLine]}> <Text type="lg-medium" style={pal.text}> - Birthday:{' '} + <Trans>Birthday: </Trans> </Text> <Link onPress={() => openModal({name: 'birth-date-settings'})}> <Text type="lg" style={pal.link}> - Show + <Trans>Show</Trans> </Text> </Link> </View> @@ -253,7 +256,7 @@ export const SettingsScreen = withAuthRequired( ) : null} <View style={[s.flexRow, styles.heading]}> <Text type="xl-bold" style={pal.text}> - Signed in as + <Trans>Signed in as</Trans> </Text> <View style={s.flex1} /> </View> @@ -282,10 +285,10 @@ export const SettingsScreen = withAuthRequired( testID="signOutBtn" onPress={isSwitching ? undefined : onPressSignout} accessibilityRole="button" - accessibilityLabel="Sign out" + accessibilityLabel={_(msg`Sign out`)} accessibilityHint={`Signs ${store.me.displayName} out of Bluesky`}> <Text type="lg" style={pal.link}> - Sign out + <Trans>Sign out</Trans> </Text> </TouchableOpacity> </View> @@ -321,7 +324,7 @@ export const SettingsScreen = withAuthRequired( style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} onPress={isSwitching ? undefined : onPressAddAccount} accessibilityRole="button" - accessibilityLabel="Add account" + accessibilityLabel={_(msg`Add account`)} accessibilityHint="Create a new Bluesky account"> <View style={[styles.iconContainer, pal.btn]}> <FontAwesomeIcon @@ -330,21 +333,21 @@ export const SettingsScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - Add account + <Trans>Add account</Trans> </Text> </TouchableOpacity> <View style={styles.spacer20} /> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Invite a Friend + <Trans>Invite a Friend</Trans> </Text> <TouchableOpacity testID="inviteFriendBtn" style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} onPress={isSwitching ? undefined : onPressInviteCodes} accessibilityRole="button" - accessibilityLabel="Invite" + accessibilityLabel={_(msg`Invite`)} accessibilityHint="Opens invite code list"> <View style={[ @@ -371,7 +374,7 @@ export const SettingsScreen = withAuthRequired( <View style={styles.spacer20} /> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Accessibility + <Trans>Accessibility</Trans> </Text> <View style={[pal.view, styles.toggleCard]}> <ToggleButton @@ -386,7 +389,7 @@ export const SettingsScreen = withAuthRequired( <View style={styles.spacer20} /> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Appearance + <Trans>Appearance</Trans> </Text> <View> <View style={[styles.linkCard, pal.view, styles.selectableBtns]}> @@ -415,7 +418,7 @@ export const SettingsScreen = withAuthRequired( <View style={styles.spacer20} /> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Basics + <Trans>Basics</Trans> </Text> <TouchableOpacity testID="preferencesHomeFeedButton" @@ -423,7 +426,7 @@ export const SettingsScreen = withAuthRequired( onPress={openHomeFeedPreferences} accessibilityRole="button" accessibilityHint="" - accessibilityLabel="Opens the home feed preferences"> + accessibilityLabel={_(msg`Opens the home feed preferences`)}> <View style={[styles.iconContainer, pal.btn]}> <FontAwesomeIcon icon="sliders" @@ -431,7 +434,7 @@ export const SettingsScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - Home Feed Preferences + <Trans>Home Feed Preferences</Trans> </Text> </TouchableOpacity> <TouchableOpacity @@ -440,7 +443,7 @@ export const SettingsScreen = withAuthRequired( onPress={openThreadsPreferences} accessibilityRole="button" accessibilityHint="" - accessibilityLabel="Opens the threads preferences"> + accessibilityLabel={_(msg`Opens the threads preferences`)}> <View style={[styles.iconContainer, pal.btn]}> <FontAwesomeIcon icon={['far', 'comments']} @@ -449,20 +452,20 @@ export const SettingsScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - Thread Preferences + <Trans>Thread Preferences</Trans> </Text> </TouchableOpacity> <TouchableOpacity testID="savedFeedsBtn" style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} accessibilityHint="My Saved Feeds" - accessibilityLabel="Opens screen with all saved feeds" + accessibilityLabel={_(msg`Opens screen with all saved feeds`)} onPress={onPressSavedFeeds}> <View style={[styles.iconContainer, pal.btn]}> <HashtagIcon style={pal.text} size={18} strokeWidth={3} /> </View> <Text type="lg" style={pal.text}> - My Saved Feeds + <Trans>My Saved Feeds</Trans> </Text> </TouchableOpacity> <TouchableOpacity @@ -471,7 +474,7 @@ export const SettingsScreen = withAuthRequired( onPress={isSwitching ? undefined : onPressLanguageSettings} accessibilityRole="button" accessibilityHint="Language settings" - accessibilityLabel="Opens configurable language settings"> + accessibilityLabel={_(msg`Opens configurable language settings`)}> <View style={[styles.iconContainer, pal.btn]}> <FontAwesomeIcon icon="language" @@ -479,7 +482,7 @@ export const SettingsScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - Languages + <Trans>Languages</Trans> </Text> </TouchableOpacity> <TouchableOpacity @@ -490,18 +493,18 @@ export const SettingsScreen = withAuthRequired( } accessibilityRole="button" accessibilityHint="" - accessibilityLabel="Opens moderation settings"> + accessibilityLabel={_(msg`Opens moderation settings`)}> <View style={[styles.iconContainer, pal.btn]}> <HandIcon style={pal.text} size={18} strokeWidth={6} /> </View> <Text type="lg" style={pal.text}> - Moderation + <Trans>Moderation</Trans> </Text> </TouchableOpacity> <View style={styles.spacer20} /> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Advanced + <Trans>Advanced</Trans> </Text> <TouchableOpacity testID="appPasswordBtn" @@ -509,7 +512,7 @@ export const SettingsScreen = withAuthRequired( onPress={onPressAppPasswords} accessibilityRole="button" accessibilityHint="Open app password settings" - accessibilityLabel="Opens the app password settings page"> + accessibilityLabel={_(msg`Opens the app password settings page`)}> <View style={[styles.iconContainer, pal.btn]}> <FontAwesomeIcon icon="lock" @@ -517,7 +520,7 @@ export const SettingsScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text}> - App passwords + <Trans>App passwords</Trans> </Text> </TouchableOpacity> <TouchableOpacity @@ -525,7 +528,7 @@ export const SettingsScreen = withAuthRequired( style={[styles.linkCard, pal.view, isSwitching && styles.dimmed]} onPress={isSwitching ? undefined : onPressChangeHandle} accessibilityRole="button" - accessibilityLabel="Change handle" + accessibilityLabel={_(msg`Change handle`)} accessibilityHint="Choose a new Bluesky username or create"> <View style={[styles.iconContainer, pal.btn]}> <FontAwesomeIcon @@ -534,19 +537,19 @@ export const SettingsScreen = withAuthRequired( /> </View> <Text type="lg" style={pal.text} numberOfLines={1}> - Change handle + <Trans>Change handle</Trans> </Text> </TouchableOpacity> <View style={styles.spacer20} /> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Danger Zone + <Trans>Danger Zone</Trans> </Text> <TouchableOpacity style={[pal.view, styles.linkCard]} onPress={onPressDeleteAccount} accessible={true} accessibilityRole="button" - accessibilityLabel="Delete account" + accessibilityLabel={_(msg`Delete account`)} accessibilityHint="Opens modal for account deletion confirmation. Requires email code."> <View style={[styles.iconContainer, dangerBg]}> <FontAwesomeIcon @@ -556,21 +559,21 @@ export const SettingsScreen = withAuthRequired( /> </View> <Text type="lg" style={dangerText}> - Delete my account… + <Trans>Delete my account…</Trans> </Text> </TouchableOpacity> <View style={styles.spacer20} /> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Developer Tools + <Trans>Developer Tools</Trans> </Text> <TouchableOpacity style={[pal.view, styles.linkCardNoIcon]} onPress={onPressSystemLog} accessibilityRole="button" accessibilityHint="Open system log" - accessibilityLabel="Opens the system log page"> + accessibilityLabel={_(msg`Opens the system log page`)}> <Text type="lg" style={pal.text}> - System log + <Trans>System log</Trans> </Text> </TouchableOpacity> {__DEV__ ? ( @@ -588,9 +591,9 @@ export const SettingsScreen = withAuthRequired( onPress={onPressStorybook} accessibilityRole="button" accessibilityHint="Open storybook page" - accessibilityLabel="Opens the storybook page"> + accessibilityLabel={_(msg`Opens the storybook page`)}> <Text type="lg" style={pal.text}> - Storybook + <Trans>Storybook</Trans> </Text> </TouchableOpacity> <TouchableOpacity @@ -598,9 +601,9 @@ export const SettingsScreen = withAuthRequired( onPress={onPressResetPreferences} accessibilityRole="button" accessibilityHint="Reset preferences" - accessibilityLabel="Resets the preferences state"> + accessibilityLabel={_(msg`Resets the preferences state`)}> <Text type="lg" style={pal.text}> - Reset preferences state + <Trans>Reset preferences state</Trans> </Text> </TouchableOpacity> <TouchableOpacity @@ -608,9 +611,9 @@ export const SettingsScreen = withAuthRequired( onPress={onPressResetOnboarding} accessibilityRole="button" accessibilityHint="Reset onboarding" - accessibilityLabel="Resets the onboarding state"> + accessibilityLabel={_(msg`Resets the onboarding state`)}> <Text type="lg" style={pal.text}> - Reset onboarding state + <Trans>Reset onboarding state</Trans> </Text> </TouchableOpacity> </> @@ -620,7 +623,9 @@ export const SettingsScreen = withAuthRequired( accessibilityRole="button" onPress={onPressBuildInfo}> <Text type="sm" style={[styles.buildInfo, pal.textLight]}> - Build version {AppInfo.appVersion} {AppInfo.updateChannel} + <Trans> + Build version {AppInfo.appVersion} {AppInfo.updateChannel} + </Trans> </Text> </TouchableOpacity> <Text type="sm" style={[pal.textLight]}> @@ -630,7 +635,7 @@ export const SettingsScreen = withAuthRequired( accessibilityRole="button" onPress={onPressStatusPage}> <Text type="sm" style={[styles.buildInfo, pal.textLight]}> - Status page + <Trans>Status page</Trans> </Text> </TouchableOpacity> </View> @@ -646,6 +651,7 @@ const EmailConfirmationNotice = observer( const pal = usePalette('default') const palInverted = usePalette('inverted') const store = useStores() + const {_} = useLingui() const {isMobile} = useWebMediaQueries() const {openModal} = useModalControls() @@ -656,7 +662,7 @@ const EmailConfirmationNotice = observer( return ( <View style={{marginBottom: 20}}> <Text type="xl-bold" style={[pal.text, styles.heading]}> - Verify email + <Trans>Verify email</Trans> </Text> <View style={[ @@ -681,7 +687,7 @@ const EmailConfirmationNotice = observer( isMobile && {flex: 1}, ]} accessibilityRole="button" - accessibilityLabel="Verify my email" + accessibilityLabel={_(msg`Verify my email`)} accessibilityHint="" onPress={() => openModal({name: 'verify-email'})}> <FontAwesomeIcon @@ -690,12 +696,12 @@ const EmailConfirmationNotice = observer( size={16} /> <Text type="button" style={palInverted.text}> - Verify My Email + <Trans>Verify My Email</Trans> </Text> </Pressable> </View> <Text style={pal.textLight}> - Protect your account by verifying your email. + <Trans>Protect your account by verifying your email.</Trans> </Text> </View> </View> diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index c8b3e0917..99e1d7d98 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -43,6 +43,8 @@ import {NavigationProp} from 'lib/routes/types' import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' import {isWeb} from 'platform/detection' import {formatCount, formatCountShortOnly} from 'view/com/util/numeric/format' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {useSetDrawerOpen} from '#/state/shell' import {useModalControls} from '#/state/modals' @@ -50,6 +52,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { const theme = useTheme() const pal = usePalette('default') const store = useStores() + const {_} = useLingui() const setDrawerOpen = useSetDrawerOpen() const navigation = useNavigation<NavigationProp>() const {track} = useAnalytics() @@ -158,7 +161,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { <View style={{}}> <TouchableOpacity testID="profileCardButton" - accessibilityLabel="Profile" + accessibilityLabel={_(msg`Profile`)} accessibilityHint="Navigates to your profile" onPress={onPressProfile}> <UserAvatar @@ -215,7 +218,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { ) } label="Search" - accessibilityLabel="Search" + accessibilityLabel={_(msg`Search`)} accessibilityHint="" bold={isAtSearch} onPress={onPressSearch} @@ -237,7 +240,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { ) } label="Home" - accessibilityLabel="Home" + accessibilityLabel={_(msg`Home`)} accessibilityHint="" bold={isAtHome} onPress={onPressHome} @@ -259,7 +262,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { ) } label="Notifications" - accessibilityLabel="Notifications" + accessibilityLabel={_(msg`Notifications`)} accessibilityHint={ notifications.unreadCountLabel === '' ? '' @@ -286,7 +289,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { ) } label="Feeds" - accessibilityLabel="Feeds" + accessibilityLabel={_(msg`Feeds`)} accessibilityHint="" bold={isAtFeeds} onPress={onPressMyFeeds} @@ -294,14 +297,14 @@ export const DrawerContent = observer(function DrawerContentImpl() { <MenuItem icon={<ListIcon strokeWidth={2} style={pal.text} size={26} />} label="Lists" - accessibilityLabel="Lists" + accessibilityLabel={_(msg`Lists`)} accessibilityHint="" onPress={onPressLists} /> <MenuItem icon={<HandIcon strokeWidth={5} style={pal.text} size={24} />} label="Moderation" - accessibilityLabel="Moderation" + accessibilityLabel={_(msg`Moderation`)} accessibilityHint="" onPress={onPressModeration} /> @@ -322,7 +325,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { ) } label="Profile" - accessibilityLabel="Profile" + accessibilityLabel={_(msg`Profile`)} accessibilityHint="" onPress={onPressProfile} /> @@ -335,7 +338,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { /> } label="Settings" - accessibilityLabel="Settings" + accessibilityLabel={_(msg`Settings`)} accessibilityHint="" onPress={onPressSettings} /> @@ -346,7 +349,7 @@ export const DrawerContent = observer(function DrawerContentImpl() { <View style={styles.footer}> <TouchableOpacity accessibilityRole="link" - accessibilityLabel="Send feedback" + accessibilityLabel={_(msg`Send feedback`)} accessibilityHint="" onPress={onPressFeedback} style={[ @@ -362,17 +365,17 @@ export const DrawerContent = observer(function DrawerContentImpl() { icon={['far', 'message']} /> <Text type="lg-medium" style={[pal.link, s.pl10]}> - Feedback + <Trans>Feedback</Trans> </Text> </TouchableOpacity> <TouchableOpacity accessibilityRole="link" - accessibilityLabel="Send feedback" + accessibilityLabel={_(msg`Send feedback`)} accessibilityHint="" onPress={onPressHelp} style={[styles.footerBtn]}> <Text type="lg-medium" style={[pal.link, s.pl10]}> - Help + <Trans>Help</Trans> </Text> </TouchableOpacity> </View> diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index db4fa9d71..69a7c4c0e 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -24,6 +24,8 @@ import {styles} from './BottomBarStyles' import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' import {UserAvatar} from 'view/com/util/UserAvatar' +import {useLingui} from '@lingui/react' +import {msg} from '@lingui/macro' import {useModalControls} from '#/state/modals' type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds' @@ -34,6 +36,7 @@ export const BottomBar = observer(function BottomBarImpl({ const {openModal} = useModalControls() const store = useStores() const pal = usePalette('default') + const {_} = useLingui() const safeAreaInsets = useSafeAreaInsets() const {track} = useAnalytics() const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} = @@ -105,7 +108,7 @@ export const BottomBar = observer(function BottomBarImpl({ } onPress={onPressHome} accessibilityRole="tab" - accessibilityLabel="Home" + accessibilityLabel={_(msg`Home`)} accessibilityHint="" /> <Btn @@ -127,7 +130,7 @@ export const BottomBar = observer(function BottomBarImpl({ } onPress={onPressSearch} accessibilityRole="search" - accessibilityLabel="Search" + accessibilityLabel={_(msg`Search`)} accessibilityHint="" /> <Btn @@ -149,7 +152,7 @@ export const BottomBar = observer(function BottomBarImpl({ } onPress={onPressFeeds} accessibilityRole="tab" - accessibilityLabel="Feeds" + accessibilityLabel={_(msg`Feeds`)} accessibilityHint="" /> <Btn @@ -173,7 +176,7 @@ export const BottomBar = observer(function BottomBarImpl({ notificationCount={notifications.unreadCountLabel} accessible={true} accessibilityRole="tab" - accessibilityLabel="Notifications" + accessibilityLabel={_(msg`Notifications`)} accessibilityHint={ notifications.unreadCountLabel === '' ? '' @@ -215,7 +218,7 @@ export const BottomBar = observer(function BottomBarImpl({ onPress={onPressProfile} onLongPress={onLongPressProfile} accessibilityRole="tab" - accessibilityLabel="Profile" + accessibilityLabel={_(msg`Profile`)} accessibilityHint="" /> </Animated.View> diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 39271605c..b85823b6f 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -39,6 +39,8 @@ import {getCurrentRoute, isTab, isStateAtTabRoot} from 'lib/routes/helpers' import {NavigationProp, CommonNavigatorParams} from 'lib/routes/types' import {router} from '../../../routes' import {makeProfileLink} from 'lib/routes/links' +import {useLingui} from '@lingui/react' +import {Trans, msg} from '@lingui/macro' const ProfileCard = observer(function ProfileCardImpl() { const store = useStores() @@ -67,6 +69,7 @@ function BackBtn() { const {isTablet} = useWebMediaQueries() const pal = usePalette('default') const navigation = useNavigation<NavigationProp>() + const {_} = useLingui() const shouldShow = useNavigationState(state => !isStateAtTabRoot(state)) const onPressBack = React.useCallback(() => { @@ -86,7 +89,7 @@ function BackBtn() { onPress={onPressBack} style={styles.backBtn} accessibilityRole="button" - accessibilityLabel="Go back" + accessibilityLabel={_(msg`Go back`)} accessibilityHint=""> <FontAwesomeIcon size={24} @@ -184,6 +187,7 @@ const NavItem = observer(function NavItemImpl({ function ComposeBtn() { const store = useStores() const {getState} = useNavigation() + const {_} = useLingui() const {isTablet} = useWebMediaQueries() const getProfileHandle = async () => { @@ -222,7 +226,7 @@ function ComposeBtn() { style={[styles.newPostBtn]} onPress={onPressCompose} accessibilityRole="button" - accessibilityLabel="New post" + accessibilityLabel={_(msg`New post`)} accessibilityHint=""> <View style={styles.newPostBtnIconWrapper}> <ComposeIcon2 @@ -232,7 +236,7 @@ function ComposeBtn() { /> </View> <Text type="button" style={styles.newPostBtnLabel}> - New Post + <Trans>New Post</Trans> </Text> </TouchableOpacity> ) diff --git a/src/view/shell/desktop/Search.tsx b/src/view/shell/desktop/Search.tsx index caecea4a8..f54858b8a 100644 --- a/src/view/shell/desktop/Search.tsx +++ b/src/view/shell/desktop/Search.tsx @@ -9,10 +9,13 @@ import {MagnifyingGlassIcon2} from 'lib/icons' import {NavigationProp} from 'lib/routes/types' import {ProfileCard} from 'view/com/profile/ProfileCard' import {Text} from 'view/com/util/text/Text' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' export const DesktopSearch = observer(function DesktopSearch() { const store = useStores() const pal = usePalette('default') + const {_} = useLingui() const textInput = React.useRef<TextInput>(null) const [isInputFocused, setIsInputFocused] = React.useState<boolean>(false) const [query, setQuery] = React.useState<string>('') @@ -75,7 +78,7 @@ export const DesktopSearch = observer(function DesktopSearch() { onChangeText={onChangeQuery} onSubmitEditing={onSubmit} accessibilityRole="search" - accessibilityLabel="Search" + accessibilityLabel={_(msg`Search`)} accessibilityHint="" /> {query ? ( @@ -83,11 +86,11 @@ export const DesktopSearch = observer(function DesktopSearch() { <TouchableOpacity onPress={onPressCancelSearch} accessibilityRole="button" - accessibilityLabel="Cancel search" + accessibilityLabel={_(msg`Cancel search`)} accessibilityHint="Exits inputting search query" onAccessibilityEscape={onPressCancelSearch}> <Text type="lg" style={[pal.link]}> - Cancel + <Trans>Cancel</Trans> </Text> </TouchableOpacity> </View> @@ -106,7 +109,7 @@ export const DesktopSearch = observer(function DesktopSearch() { ) : ( <View> <Text style={[pal.textLight, styles.noResults]}> - No results found for {autocompleteView.prefix} + <Trans>No results found for {autocompleteView.prefix}</Trans> </Text> </View> )} diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 10489489e..792499521 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -17,6 +17,7 @@ import {BottomBarWeb} from './bottom-bar/BottomBarWeb' import {useNavigation} from '@react-navigation/native' import {NavigationProp} from 'lib/routes/types' import {useAuxClick} from 'lib/hooks/useAuxClick' +import {t} from '@lingui/macro' import { useIsDrawerOpen, useSetDrawerOpen, @@ -73,7 +74,7 @@ const ShellInner = observer(function ShellInnerImpl() { <TouchableOpacity onPress={() => setDrawerOpen(false)} style={styles.drawerMask} - accessibilityLabel="Close navigation footer" + accessibilityLabel={t`Close navigation footer`} accessibilityHint="Closes bottom navigation bar"> <View style={styles.drawerContainer}> <DrawerContent /> diff --git a/yarn.lock b/yarn.lock index cd4f71a7a..fd7cd19be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -353,6 +353,14 @@ "@babel/highlight" "^7.22.10" chalk "^2.4.2" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" @@ -379,6 +387,27 @@ json5 "^2.2.2" semver "^6.3.1" +"@babel/core@^7.21.0", "@babel/core@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.2.tgz#ed10df0d580fff67c5f3ee70fd22e2e4c90a9f94" + integrity sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.0" + "@babel/helpers" "^7.23.2" + "@babel/parser" "^7.23.0" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.23.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/eslint-parser@^7.16.3", "@babel/eslint-parser@^7.18.2": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.10.tgz#bfdf3d1b32ad573fe7c1c3447e0b485e3a41fd09" @@ -398,6 +427,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.21.1", "@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -423,6 +462,17 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.10", "@babel/helper-create-class-features-plugin@^7.22.5": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" @@ -463,6 +513,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-function-name@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" @@ -471,6 +526,14 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.5" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" @@ -492,6 +555,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" @@ -503,6 +573,17 @@ "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.5" +"@babel/helper-module-transforms@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz#3ec246457f6c842c0aee62a01f60739906f7047e" + integrity sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" @@ -559,11 +640,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + "@babel/helper-validator-option@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" @@ -587,6 +678,15 @@ "@babel/traverse" "^7.22.10" "@babel/types" "^7.22.10" +"@babel/helpers@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767" + integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.23.0" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.22.10": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" @@ -596,11 +696,25 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== +"@babel/parser@^7.21.2", "@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" @@ -1517,7 +1631,7 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.21.0": +"@babel/runtime@^7.20.13", "@babel/runtime@^7.21.0": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== @@ -1533,6 +1647,15 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + "@babel/traverse@^7.20.0", "@babel/traverse@^7.22.10", "@babel/traverse@^7.7.2", "@babel/traverse@^7.7.4": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" @@ -1549,6 +1672,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" @@ -1558,6 +1697,15 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@babel/types@^7.21.2", "@babel/types@^7.22.15", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@bam.tech/react-native-image-resizer@^3.0.4": version "3.0.5" resolved "https://registry.yarnpkg.com/@bam.tech/react-native-image-resizer/-/react-native-image-resizer-3.0.5.tgz#6661ba020de156268f73bdc92fbb93ef86f88a13" @@ -1788,6 +1936,116 @@ resolved "https://registry.yarnpkg.com/@emoji-mart/react/-/react-1.1.1.tgz#ddad52f93a25baf31c5383c3e7e4c6e05554312a" integrity sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g== +"@esbuild/android-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" + integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== + +"@esbuild/android-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" + integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== + +"@esbuild/android-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" + integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== + +"@esbuild/darwin-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" + integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + +"@esbuild/darwin-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" + integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== + +"@esbuild/freebsd-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" + integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== + +"@esbuild/freebsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" + integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== + +"@esbuild/linux-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" + integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== + +"@esbuild/linux-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" + integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== + +"@esbuild/linux-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" + integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== + +"@esbuild/linux-loong64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" + integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== + +"@esbuild/linux-mips64el@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" + integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== + +"@esbuild/linux-ppc64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" + integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== + +"@esbuild/linux-riscv64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" + integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== + +"@esbuild/linux-s390x@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" + integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== + +"@esbuild/linux-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== + +"@esbuild/netbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" + integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== + +"@esbuild/openbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" + integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== + +"@esbuild/sunos-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" + integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== + +"@esbuild/win32-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" + integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== + +"@esbuild/win32-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" + integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== + +"@esbuild/win32-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" + integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -2842,6 +3100,103 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== +"@lingui/babel-plugin-extract-messages@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.5.0.tgz#71e56cc2eae73890caeea15a00ae4965413430ec" + integrity sha512-jZq3Gbi691jsHyQ4+OPnGgIqZt5eKEGnmI75akYlZpwTPxF7n+hiuKlQS+YB3xfKvcvlAED76ZAMCcwYG5fNrQ== + +"@lingui/cli@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/cli/-/cli-4.5.0.tgz#fb8685cb682cf624b6e5b6d1207f8127b98ed95a" + integrity sha512-MzhxNUNd+YYEmK79TwmneUow5BuLwpOlrUrZq9EyIAWUM4N6kkCVkZ8VIMYCL4TXGQ4kBQjstgDpkF8wdFRtNg== + dependencies: + "@babel/core" "^7.21.0" + "@babel/generator" "^7.21.1" + "@babel/parser" "^7.21.2" + "@babel/runtime" "^7.21.0" + "@babel/types" "^7.21.2" + "@lingui/babel-plugin-extract-messages" "4.5.0" + "@lingui/conf" "4.5.0" + "@lingui/core" "4.5.0" + "@lingui/format-po" "4.5.0" + "@lingui/message-utils" "4.5.0" + babel-plugin-macros "^3.0.1" + chalk "^4.1.0" + chokidar "3.5.1" + cli-table "0.3.6" + commander "^10.0.0" + convert-source-map "^2.0.0" + date-fns "^2.16.1" + esbuild "^0.17.10" + glob "^7.1.4" + inquirer "^7.3.3" + micromatch "4.0.2" + normalize-path "^3.0.0" + ora "^5.1.0" + pathe "^1.1.0" + pkg-up "^3.1.0" + pofile "^1.1.4" + pseudolocale "^2.0.0" + ramda "^0.27.1" + source-map "^0.8.0-beta.0" + +"@lingui/conf@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/conf/-/conf-4.5.0.tgz#09b39de3a03a9017cd8299b1d8df923c78447ebf" + integrity sha512-OBm4RQQtbpvmuazLWVpvpaOpt/xvu1PBv8WUX8QoW1vsROe/3P5BpRHRYFyMeZz5mhORJgis9lQtDTq145Ruug== + dependencies: + "@babel/runtime" "^7.20.13" + chalk "^4.1.0" + cosmiconfig "^8.0.0" + jest-validate "^29.4.3" + jiti "^1.17.1" + lodash.get "^4.4.2" + +"@lingui/core@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/core/-/core-4.5.0.tgz#8b907238bd0b420b372272d0a757c56bed3aed14" + integrity sha512-8zTuIXJo5Qvjato7LWE6Q4RHiO4LjTBVOoRlqfOGYDp8VZ9w9P7Z7IJgxI7UP5Z1wiuEvnMdVF9I1C4acqXGlQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@lingui/message-utils" "4.5.0" + unraw "^3.0.0" + +"@lingui/format-po@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/format-po/-/format-po-4.5.0.tgz#88dabeaa565ce5e4ff2a1b9865da7759b393af27" + integrity sha512-xQNzZ4RCQfh6TjzjUsyHz3B0R9FJuzhBit9R37NyMn6mL3kBTCUExpPczknm8gWZjtfFO4T8EH5eJhhC5vgJYg== + dependencies: + "@lingui/conf" "4.5.0" + "@lingui/message-utils" "4.5.0" + date-fns "^2.29.3" + pofile "^1.1.4" + +"@lingui/macro@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/macro/-/macro-4.5.0.tgz#4e779f4652200b7ecc6f3364fda840ae365fb996" + integrity sha512-6qha9YXuNnta4HCR+g6J6UPaAuAFlM1duqgznh4X7hHSsFG+m6oX7/srAMfU41Z8lbDmgXc3raqHLXFSdUNbYQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@babel/types" "^7.20.7" + "@lingui/conf" "4.5.0" + "@lingui/core" "4.5.0" + "@lingui/message-utils" "4.5.0" + +"@lingui/message-utils@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/message-utils/-/message-utils-4.5.0.tgz#7ae9dc6cb65cbb5e2dc1b8cdcc4c8b92d5c7189f" + integrity sha512-iRqh2wvNtzJO3NStB77nEXEfeI53aVVjzD7/mBrEm/P0lC7sqPHk0WBQCfzE0N9xm6a+XHmHu3J+x2nnQ2OjcA== + dependencies: + "@messageformat/parser" "^5.0.0" + +"@lingui/react@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@lingui/react/-/react-4.5.0.tgz#1cae72e89890f8d270b37cdde051a3c69df97727" + integrity sha512-dv/oxBshyaVJ3XzbPDnWn3abhwtaS1sx8cEO2qDjs+OhW0AeWD9hyVDrduf5SBIuXFJfJQNNA8+2P2nO0lxRbQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@lingui/core" "4.5.0" + "@lukeed/csprng@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.1.0.tgz#1e3e4bd05c1cc7a0b2ddbd8a03f39f6e4b5e6cfe" @@ -2861,6 +3216,13 @@ dependencies: semver "7.5.4" +"@messageformat/parser@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.0.tgz#05e4851c782d633ad735791dd0a68ee65d2a7201" + integrity sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ== + dependencies: + moo "^0.5.1" + "@miblanchard/react-native-slider@^2.3.1": version "2.3.1" resolved "https://registry.yarnpkg.com/@miblanchard/react-native-slider/-/react-native-slider-2.3.1.tgz#79e0f1f9b1ce43ef25ee51ee9256c012e5dfa412" @@ -6183,7 +6545,7 @@ any-promise@^1.0.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== -anymatch@^3.0.3, anymatch@~3.1.2: +anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -6578,7 +6940,7 @@ babel-plugin-jest-hoist@^29.6.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-macros@^3.1.0: +babel-plugin-macros@^3.0.1, babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== @@ -6945,7 +7307,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -7235,6 +7597,11 @@ char-regex@^2.0.0: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.1.tgz#6dafdb25f9d3349914079f010ba8d0e6ff9cd01e" integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw== +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + charenc@0.0.2, charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" @@ -7254,6 +7621,21 @@ child-process-promise@^2.2.0: node-version "^1.0.0" promise-polyfill "^6.0.1" +chokidar@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chokidar@^3.4.2, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -7344,6 +7726,13 @@ cli-spinners@^2.0.0, cli-spinners@^2.5.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== +cli-table@0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.6.tgz#e9d6aa859c7fe636981fd3787378c2a20bce92fc" + integrity sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ== + dependencies: + colors "1.0.3" + cli-truncate@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" @@ -7352,6 +7741,11 @@ cli-truncate@^3.1.0: slice-ansi "^5.0.0" string-width "^5.0.0" +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -7477,6 +7871,11 @@ colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.20: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -7499,7 +7898,7 @@ commander@2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== -commander@^10.0.1: +commander@^10.0.0, commander@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== @@ -7710,6 +8109,16 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^8.0.0: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -8021,7 +8430,7 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" -date-fns@^2.30.0: +date-fns@^2.16.1, date-fns@^2.29.3, date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== @@ -8817,6 +9226,34 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw== +esbuild@^0.17.10: + version "0.17.19" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955" + integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== + optionalDependencies: + "@esbuild/android-arm" "0.17.19" + "@esbuild/android-arm64" "0.17.19" + "@esbuild/android-x64" "0.17.19" + "@esbuild/darwin-arm64" "0.17.19" + "@esbuild/darwin-x64" "0.17.19" + "@esbuild/freebsd-arm64" "0.17.19" + "@esbuild/freebsd-x64" "0.17.19" + "@esbuild/linux-arm" "0.17.19" + "@esbuild/linux-arm64" "0.17.19" + "@esbuild/linux-ia32" "0.17.19" + "@esbuild/linux-loong64" "0.17.19" + "@esbuild/linux-mips64el" "0.17.19" + "@esbuild/linux-ppc64" "0.17.19" + "@esbuild/linux-riscv64" "0.17.19" + "@esbuild/linux-s390x" "0.17.19" + "@esbuild/linux-x64" "0.17.19" + "@esbuild/netbsd-x64" "0.17.19" + "@esbuild/openbsd-x64" "0.17.19" + "@esbuild/sunos-x64" "0.17.19" + "@esbuild/win32-arm64" "0.17.19" + "@esbuild/win32-ia32" "0.17.19" + "@esbuild/win32-x64" "0.17.19" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -9603,6 +10040,15 @@ express@^4.17.2, express@^4.17.3, express@^4.18.2: utils-merge "1.0.1" vary "~1.1.2" +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + fast-base64-decode@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" @@ -9741,6 +10187,13 @@ fetch-retry@^4.1.1: resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-4.1.1.tgz#fafe0bb22b54f4d0a9c788dff6dd7f8673ca63f3" integrity sha512-e6eB7zN6UBSwGVwrbWVH+gdLnkW9WwHhmq2YDK1Sh30pzx1onRVGBvogTlUeWxwTa+L86NYdo4hFkh7O8ZjSnA== +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -10068,7 +10521,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -10183,7 +10636,7 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== -glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -10674,7 +11127,7 @@ hyphenate-style-name@^1.0.0, hyphenate-style-name@^1.0.3: resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -10740,7 +11193,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.1.0, import-fresh@^3.2.1: +import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -10802,6 +11255,25 @@ inline-style-prefixer@^6.0.1: css-in-js-utils "^3.1.0" fast-loops "^1.1.3" +inquirer@^7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + internal-ip@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -12113,6 +12585,18 @@ jest-validate@^29.2.1, jest-validate@^29.6.3: leven "^3.1.0" pretty-format "^29.6.3" +jest-validate@^29.4.3: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + jest-watch-select-projects@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/jest-watch-select-projects/-/jest-watch-select-projects-2.0.0.tgz#4373d7e4de862aae28b46e036b669a4c913ea867" @@ -12250,6 +12734,11 @@ jimp-compact@0.16.1: resolved "https://registry.yarnpkg.com/jimp-compact/-/jimp-compact-0.16.1.tgz#9582aea06548a2c1e04dd148d7c3ab92075aefa3" integrity sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww== +jiti@^1.17.1: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + jiti@^1.18.2: version "1.19.3" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.19.3.tgz#ef554f76465b3c2b222dc077834a71f0d4a37569" @@ -12803,6 +13292,11 @@ lodash.defaults@^4.2.0: resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -13447,6 +13941,14 @@ metro@0.76.8: ws "^7.5.1" yargs "^17.6.2" +micromatch@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -13618,6 +14120,11 @@ moment@^2.19.3: resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== +moo@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -13659,6 +14166,11 @@ multipipe@^4.0.0: duplexer2 "^0.1.2" object-assign "^4.1.0" +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + mv@~2: version "2.1.1" resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" @@ -14150,7 +14662,7 @@ ora@3.4.0: strip-ansi "^5.2.0" wcwidth "^1.0.1" -ora@^5.4.1: +ora@^5.1.0, ora@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== @@ -14447,6 +14959,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathe@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" + integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== + peek-readable@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" @@ -14525,7 +15042,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -14669,6 +15186,11 @@ pngjs@^3.3.0: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== +pofile@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.1.4.tgz#eab7e29f5017589b2a61b2259dff608c0cad76a2" + integrity sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g== + postcss-attribute-case-insensitive@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz#03d761b24afc04c09e757e92ff53716ae8ea2741" @@ -15343,6 +15865,15 @@ pretty-format@^29.0.0, pretty-format@^29.0.3, pretty-format@^29.6.3: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -15580,6 +16111,13 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +pseudolocale@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pseudolocale/-/pseudolocale-2.0.0.tgz#4dbe725a6b5a6fb71aba8ba64ae2be71f5267316" + integrity sha512-g1K9tCQYY4e3UGtnW8qs3kGWAOONxt7i5wuOFvf3N1EIIRhiLVIhZ9AM/ZyGTxsp231JbFywJU/EbJ5ZoqnZdg== + dependencies: + commander "^10.0.0" + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -15664,6 +16202,11 @@ raf@^3.4.1: dependencies: performance-now "^2.1.0" +ramda@^0.27.1: + version "0.27.2" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.2.tgz#84463226f7f36dc33592f6f4ed6374c48306c3f1" + integrity sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -16200,6 +16743,13 @@ readable-web-to-node-stream@^3.0.0: dependencies: readable-stream "^3.6.0" +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -16606,6 +17156,11 @@ rtl-detect@^1.0.2: resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" integrity sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ== +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -16613,6 +17168,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + rxjs@^7.5.2: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" @@ -17854,7 +18416,7 @@ through2@^2.0.1: readable-stream "~2.3.6" xtend "~4.0.1" -through@2: +through@2, through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -18017,7 +18579,7 @@ tsconfig-paths@^3.14.2: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.3: +tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -18300,6 +18862,11 @@ unquote@~1.1.1: resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg== +unraw@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unraw/-/unraw-3.0.0.tgz#73443ed70d2ab09ccbac2b00525602d5991fbbe3" + integrity sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg== + upath@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" |