diff --git a/__e2e__/mock-server.ts b/__e2e__/mock-server.ts index 482df6ef63..1bf240ccbd 100644 --- a/__e2e__/mock-server.ts +++ b/__e2e__/mock-server.ts @@ -14,7 +14,8 @@ async function main() { await server?.close() console.log('Starting new server') const inviteRequired = url?.query && 'invite' in url.query - server = await createServer({inviteRequired}) + const phoneRequired = url?.query && 'phone' in url.query + server = await createServer({inviteRequired, phoneRequired}) console.log('Listening at', server.pdsUrl) if (url?.query) { if ('users' in url.query) { diff --git a/__e2e__/tests/create-account.test.ts b/__e2e__/tests/create-account.test.ts index eab3e538b3..a6724e8e45 100644 --- a/__e2e__/tests/create-account.test.ts +++ b/__e2e__/tests/create-account.test.ts @@ -16,14 +16,12 @@ describe('Create account', () => { await element(by.id('createAccountButton')).tap() await device.takeScreenshot('1- opened create account screen') - await element(by.id('otherServerBtn')).tap() + await element(by.id('selectServiceButton')).tap() await device.takeScreenshot('2- selected other server') - await element(by.id('customServerInput')).clearText() - await element(by.id('customServerInput')).typeText(service) + await element(by.id('customServerTextInput')).typeText(service) + await element(by.id('customServerTextInput')).tapReturnKey() + await element(by.id('customServerSelectBtn')).tap() await device.takeScreenshot('3- input test server URL') - - await element(by.id('nextBtn')).tap() - await element(by.id('emailInput')).typeText('example@test.com') await element(by.id('passwordInput')).typeText('hunter2') await device.takeScreenshot('4- entered account details') @@ -31,7 +29,7 @@ describe('Create account', () => { await element(by.id('nextBtn')).tap() await element(by.id('handleInput')).typeText('e2e-test') - await device.takeScreenshot('4- entered handle') + await device.takeScreenshot('5- entered handle') await element(by.id('nextBtn')).tap() diff --git a/__e2e__/tests/invite-codes.test.ts b/__e2e__/tests/invite-codes.test.ts index 7db7c595a9..7ab5b1477b 100644 --- a/__e2e__/tests/invite-codes.test.ts +++ b/__e2e__/tests/invite-codes.test.ts @@ -1,10 +1,5 @@ /* eslint-env detox/detox */ -/** - * This test is being skipped until we can resolve the detox crash issue - * with the side drawer. - */ - import {describe, beforeAll, it} from '@jest/globals' import {expect} from 'detox' import {openApp, loginAsAlice, createServer} from '../util' @@ -31,12 +26,12 @@ describe('invite-codes', () => { await element(by.id('e2eOpenLoggedOutView')).tap() await element(by.id('createAccountButton')).tap() await device.takeScreenshot('1- opened create account screen') - await element(by.id('otherServerBtn')).tap() + await element(by.id('selectServiceButton')).tap() await device.takeScreenshot('2- selected other server') - await element(by.id('customServerInput')).clearText() - await element(by.id('customServerInput')).typeText(service) + await element(by.id('customServerTextInput')).typeText(service) + await element(by.id('customServerTextInput')).tapReturnKey() + await element(by.id('customServerSelectBtn')).tap() await device.takeScreenshot('3- input test server URL') - await element(by.id('nextBtn')).tap() await element(by.id('inviteCodeInput')).typeText(inviteCode) await element(by.id('emailInput')).typeText('example@test.com') await element(by.id('passwordInput')).typeText('hunter2') diff --git a/__e2e__/tests/invites-and-text-verification.test.ts b/__e2e__/tests/invites-and-text-verification.test.ts new file mode 100644 index 0000000000..850ca6d5c7 --- /dev/null +++ b/__e2e__/tests/invites-and-text-verification.test.ts @@ -0,0 +1,57 @@ +/* eslint-env detox/detox */ + +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' +import {openApp, loginAsAlice, createServer} from '../util' + +describe('invite-codes', () => { + let service: string + let inviteCode = '' + beforeAll(async () => { + service = await createServer('?users&invite&phone') + await openApp({permissions: {notifications: 'YES'}}) + }) + + it('I can fetch invite codes', async () => { + await loginAsAlice() + await element(by.id('e2eOpenInviteCodesModal')).tap() + await expect(element(by.id('inviteCodesModal'))).toBeVisible() + const attrs = await element(by.id('inviteCode-0-code')).getAttributes() + inviteCode = attrs.text + await element(by.id('closeBtn')).tap() + await element(by.id('e2eSignOut')).tap() + }) + + it('I can create a new account with the invite code', async () => { + await element(by.id('e2eOpenLoggedOutView')).tap() + await element(by.id('createAccountButton')).tap() + await device.takeScreenshot('1- opened create account screen') + await element(by.id('selectServiceButton')).tap() + await device.takeScreenshot('2- selected other server') + await element(by.id('customServerTextInput')).typeText(service) + await element(by.id('customServerTextInput')).tapReturnKey() + await element(by.id('customServerSelectBtn')).tap() + await device.takeScreenshot('3- input test server URL') + await element(by.id('inviteCodeInput')).typeText(inviteCode) + await element(by.id('emailInput')).typeText('example@test.com') + await element(by.id('passwordInput')).typeText('hunter2') + await device.takeScreenshot('4- entered account details') + await element(by.id('nextBtn')).tap() + await element(by.id('phoneInput')).typeText('5558675309') + await element(by.id('requestCodeBtn')).tap() + await device.takeScreenshot('5- requested code') + await element(by.id('codeInput')).typeText('000000') + await device.takeScreenshot('6- entered code') + await element(by.id('nextBtn')).tap() + await element(by.id('handleInput')).typeText('e2e-test') + await device.takeScreenshot('7- entered handle') + await element(by.id('nextBtn')).tap() + await expect(element(by.id('welcomeOnboarding'))).toBeVisible() + await element(by.id('continueBtn')).tap() + await expect(element(by.id('recommendedFeedsOnboarding'))).toBeVisible() + await element(by.id('continueBtn')).tap() + await expect(element(by.id('recommendedFollowsOnboarding'))).toBeVisible() + await element(by.id('continueBtn')).tap() + await expect(element(by.id('homeScreen'))).toBeVisible() + }) +}) diff --git a/__e2e__/tests/text-verification.test.ts b/__e2e__/tests/text-verification.test.ts new file mode 100644 index 0000000000..a307a95ffc --- /dev/null +++ b/__e2e__/tests/text-verification.test.ts @@ -0,0 +1,85 @@ +/* eslint-env detox/detox */ + +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' +import {openApp, createServer} from '../util' + +describe('Create account', () => { + let service: string + beforeAll(async () => { + service = await createServer('?phone') + await openApp({permissions: {notifications: 'YES'}}) + }) + + it('I can create a new account with text verification', async () => { + await element(by.id('e2eOpenLoggedOutView')).tap() + + await element(by.id('createAccountButton')).tap() + await device.takeScreenshot('1- opened create account screen') + await element(by.id('selectServiceButton')).tap() + await device.takeScreenshot('2- selected other server') + await element(by.id('customServerTextInput')).typeText(service) + await element(by.id('customServerTextInput')).tapReturnKey() + await element(by.id('customServerSelectBtn')).tap() + await device.takeScreenshot('3- input test server URL') + await element(by.id('emailInput')).typeText('text-verification@test.com') + await element(by.id('passwordInput')).typeText('hunter2') + await device.takeScreenshot('4- entered account details') + await element(by.id('nextBtn')).tap() + + await element(by.id('phoneInput')).typeText('1234567890') + await element(by.id('requestCodeBtn')).tap() + await device.takeScreenshot('5- requested code') + + await element(by.id('codeInput')).typeText('000000') + await device.takeScreenshot('6- entered code') + await element(by.id('nextBtn')).tap() + + await element(by.id('handleInput')).typeText('text-verification-test') + await device.takeScreenshot('7- entered handle') + + await element(by.id('nextBtn')).tap() + + await expect(element(by.id('welcomeOnboarding'))).toBeVisible() + await element(by.id('continueBtn')).tap() + await expect(element(by.id('recommendedFeedsOnboarding'))).toBeVisible() + await element(by.id('continueBtn')).tap() + await expect(element(by.id('recommendedFollowsOnboarding'))).toBeVisible() + await element(by.id('continueBtn')).tap() + await expect(element(by.id('homeScreen'))).toBeVisible() + }) + + it('failed text verification correctly goes back to the code input screen', async () => { + await element(by.id('e2eSignOut')).tap() + await element(by.id('e2eOpenLoggedOutView')).tap() + + await element(by.id('createAccountButton')).tap() + await device.takeScreenshot('1- opened create account screen') + await element(by.id('selectServiceButton')).tap() + await device.takeScreenshot('2- selected other server') + await element(by.id('customServerTextInput')).typeText(service) + await element(by.id('customServerTextInput')).tapReturnKey() + await element(by.id('customServerSelectBtn')).tap() + await device.takeScreenshot('3- input test server URL') + await element(by.id('emailInput')).typeText('text-verification2@test.com') + await element(by.id('passwordInput')).typeText('hunter2') + await device.takeScreenshot('4- entered account details') + await element(by.id('nextBtn')).tap() + + await element(by.id('phoneInput')).typeText('1234567890') + await element(by.id('requestCodeBtn')).tap() + await device.takeScreenshot('5- requested code') + + await element(by.id('codeInput')).typeText('111111') + await device.takeScreenshot('6- entered code') + await element(by.id('nextBtn')).tap() + + await element(by.id('handleInput')).typeText('text-verification-test2') + await device.takeScreenshot('7- entered handle') + + await element(by.id('nextBtn')).tap() + + await expect(element(by.id('codeInput'))).toBeVisible() + await device.takeScreenshot('8- got error') + }) +}) diff --git a/__e2e__/util.ts b/__e2e__/util.ts index c5668d042e..8c47406c07 100644 --- a/__e2e__/util.ts +++ b/__e2e__/util.ts @@ -105,7 +105,7 @@ async function openAppForDebugBuild(platform: string, opts: any) { await sleep(3000) } -export async function createServer(path = '') { +export async function createServer(path = ''): Promise { return new Promise(function (resolve, reject) { var req = http.request( { diff --git a/__tests__/lib/string.test.ts b/__tests__/lib/string.test.ts index edddcb3e09..b3aa48f4ac 100644 --- a/__tests__/lib/string.test.ts +++ b/__tests__/lib/string.test.ts @@ -463,44 +463,44 @@ describe('parseEmbedPlayerFromUrl', () => { type: 'youtube_video', source: 'youtube', playerUri: - 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1', + 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1&start=0', }, { type: 'youtube_video', source: 'youtube', playerUri: - 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1', + 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1&start=0', }, { type: 'youtube_video', source: 'youtube', playerUri: - 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1', + 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1&start=0', }, { type: 'youtube_video', source: 'youtube', playerUri: - 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1', + 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1&start=0', }, { type: 'youtube_video', source: 'youtube', playerUri: - 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1', + 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1&start=0', }, { type: 'youtube_short', source: 'youtubeShorts', hideDetails: true, playerUri: - 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1', + 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1&start=0', }, { type: 'youtube_video', source: 'youtube', playerUri: - 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1', + 'https://www.youtube.com/embed/videoId?autoplay=1&playsinline=1&start=0', }, undefined, diff --git a/app.config.js b/app.config.js index afd6677ef1..4adeb03b79 100644 --- a/app.config.js +++ b/app.config.js @@ -25,7 +25,7 @@ module.exports = function () { /** * Android build number. Must be incremented for each release. */ - const ANDROID_VERSION_CODE = 55 + const ANDROID_VERSION_CODE = 57 /** * Uses built-in Expo env vars diff --git a/assets/icons/arrowTopRight_stoke2_corner0_rounded.svg b/assets/icons/arrowTopRight_stoke2_corner0_rounded.svg new file mode 100644 index 0000000000..554a7374ec --- /dev/null +++ b/assets/icons/arrowTopRight_stoke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/calendarDays_stroke2_corner0_rounded.svg b/assets/icons/calendarDays_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..09d9c0f490 --- /dev/null +++ b/assets/icons/calendarDays_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/colorPalette_stroke2_corner0_rounded.svg b/assets/icons/colorPalette_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..b1056e1a96 --- /dev/null +++ b/assets/icons/colorPalette_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/globe_stroke2_corner0_rounded.svg b/assets/icons/globe_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..83cb88d136 --- /dev/null +++ b/assets/icons/globe_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/bskyweb/cmd/bskyweb/rss.go b/bskyweb/cmd/bskyweb/rss.go index 64e67fd228..b756b90e6a 100644 --- a/bskyweb/cmd/bskyweb/rss.go +++ b/bskyweb/cmd/bskyweb/rss.go @@ -80,7 +80,7 @@ func (srv *Server) WebProfileRSS(c echo.Context) error { } } - af, err := appbsky.FeedGetAuthorFeed(ctx, srv.xrpcc, did.String(), "", "", 30) + af, err := appbsky.FeedGetAuthorFeed(ctx, srv.xrpcc, did.String(), "", "posts_no_replies", 30) if err != nil { log.Warn("failed to fetch author feed", "did", did, "err", err) return err diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html index 57ad064f8c..50dbaa24bb 100644 --- a/bskyweb/templates/base.html +++ b/bskyweb/templates/base.html @@ -39,30 +39,49 @@ height: calc(100% + env(safe-area-inset-top)); } + /* Remove autofill styles on Webkit */ + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus, + textarea:-webkit-autofill, + textarea:-webkit-autofill:hover, + textarea:-webkit-autofill:focus, + select:-webkit-autofill, + select:-webkit-autofill:hover, + select:-webkit-autofill:focus { + border: 0; + -webkit-text-fill-color: transparent; + -webkit-box-shadow: none; + } + /* Force left-align date/time inputs on iOS mobile */ + input::-webkit-date-and-time-value { + text-align: left; + } + /* Color theming */ :root { --text: black; --background: white; - --backgroundLight: #F3F3F8; + --backgroundLight: hsl(211, 20%, 95%); } html.colorMode--dark { --text: white; - --background: black; - --backgroundLight: #26272D; + --background: hsl(211, 20%, 4%); + --backgroundLight: hsl(211, 20%, 20%); color-scheme: dark; } @media (prefers-color-scheme: light) { html.colorMode--system { --text: black; --background: white; - --backgroundLight: #F3F3F8; + --backgroundLight: hsl(211, 20%, 95%); } } @media (prefers-color-scheme: dark) { html.colorMode--system { --text: white; - --background: black; - --backgroundLight: #26272D; + --background: hsl(211, 20%, 4%); + --backgroundLight: hsl(211, 20%, 20%); color-scheme: dark; } } diff --git a/jest/test-pds.ts b/jest/test-pds.ts index d86ebd787a..1912ffc6a7 100644 --- a/jest/test-pds.ts +++ b/jest/test-pds.ts @@ -1,7 +1,7 @@ import net from 'net' import path from 'path' import fs from 'fs' -import {TestNetwork} from '@atproto/dev-env' +import {TestNetwork, TestPds} from '@atproto/dev-env' import {AtUri, BskyAgent} from '@atproto/api' export interface TestUser { @@ -55,19 +55,36 @@ class StringIdGenerator { const ids = new StringIdGenerator() export async function createServer( - {inviteRequired}: {inviteRequired: boolean} = {inviteRequired: false}, + { + inviteRequired, + phoneRequired, + }: {inviteRequired: boolean; phoneRequired: boolean} = { + inviteRequired: false, + phoneRequired: false, + }, ): Promise { - const port = await getPort() + const port = 3000 const port2 = await getPort(port + 1) const port3 = await getPort(port2 + 1) const pdsUrl = `http://localhost:${port}` const id = ids.next() + const phoneParams = phoneRequired + ? { + phoneVerificationRequired: true, + twilioAccountSid: 'ACXXXXXXX', + twilioAuthToken: 'AUTH', + twilioServiceSid: 'VAXXXXXXXX', + } + : {} + const testNet = await TestNetwork.create({ pds: { port, hostname: 'localhost', + dbPostgresSchema: `pds_${id}`, inviteRequired, + ...phoneParams, }, bsky: { dbPostgresSchema: `bsky_${id}`, @@ -76,6 +93,7 @@ export async function createServer( }, plc: {port: port2}, }) + mockTwilio(testNet.pds) const pic = fs.readFileSync( path.join(__dirname, '..', 'assets', 'default-avatar.png'), @@ -144,6 +162,8 @@ class Mocker { email, handle: name + '.test', password: 'hunter2', + verificationPhone: '1234567890', + verificationCode: '000000', }) await agent.upsertProfile(async () => { const blob = await agent.uploadBlob(this.pic, { @@ -430,3 +450,15 @@ async function getPort(start = 3000) { } throw new Error('Unable to find an available port') } + +export const mockTwilio = (pds: TestPds) => { + if (!pds.ctx.twilio) return + + pds.ctx.twilio.sendCode = async (_number: string) => { + // do nothing + } + + pds.ctx.twilio.verifyCode = async (_number: string, code: string) => { + return code === '000000' + } +} diff --git a/lingui.config.js b/lingui.config.js index 882c487741..0659bd0787 100644 --- a/lingui.config.js +++ b/lingui.config.js @@ -1,6 +1,18 @@ /** @type {import('@lingui/conf').LinguiConfig} */ module.exports = { - locales: ['en', 'de', 'es', 'fr', 'hi', 'id', 'ja', 'ko', 'pt-BR', 'uk'], + locales: [ + 'en', + 'de', + 'es', + 'fr', + 'hi', + 'id', + 'ja', + 'ko', + 'pt-BR', + 'uk', + 'ca', + ], catalogs: [ { path: '/src/locale/locales/{locale}/messages', diff --git a/package.json b/package.json index 7b4bd6aefa..f6245ac8dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bsky.app", - "version": "1.64.0", + "version": "1.65.0", "private": true, "engines": { "node": ">=18" @@ -39,7 +39,7 @@ "nuke": "rm -rf ./node_modules && rm -rf ./ios && rm -rf ./android" }, "dependencies": { - "@atproto/api": "^0.8.0", + "@atproto/api": "^0.9.1", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@emoji-mart/react": "^1.1.1", @@ -70,6 +70,7 @@ "@segment/analytics-react-native": "^2.10.1", "@segment/sovran-react-native": "^0.4.5", "@sentry/react-native": "5.5.0", + "@tamagui/focus-scope": "^1.84.1", "@tanstack/react-query": "^5.8.1", "@tiptap/core": "^2.0.0-beta.220", "@tiptap/extension-document": "^2.0.0-beta.220", @@ -119,6 +120,7 @@ "js-sha256": "^0.9.0", "jwt-decode": "^4.0.0", "lande": "^1.0.10", + "libphonenumber-js": "^1.10.53", "lodash.chunk": "^4.2.0", "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", diff --git a/patches/@lingui+core+4.5.0.patch b/patches/@lingui+core+4.5.0.patch new file mode 100644 index 0000000000..8ace93a744 --- /dev/null +++ b/patches/@lingui+core+4.5.0.patch @@ -0,0 +1,10 @@ +diff --git a/node_modules/@lingui/core/dist/index.mjs b/node_modules/@lingui/core/dist/index.mjs +index 9759736..881f67b 100644 +--- a/node_modules/@lingui/core/dist/index.mjs ++++ b/node_modules/@lingui/core/dist/index.mjs +@@ -1,4 +1,4 @@ +-import unraw from 'unraw'; ++import { unraw } from 'unraw'; + import { compileMessage } from '@lingui/message-utils/compileMessage'; + + const isString = (s) => typeof s === "string"; diff --git a/src/App.native.tsx b/src/App.native.tsx index 9de901767a..41b78fc98a 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -13,6 +13,8 @@ import { import 'view/icons' +import {ThemeProvider as Alf} from '#/alf' +import {useColorModeTheme} from '#/alf/util/useColorModeTheme' import {init as initPersistedState} from '#/state/persisted' import {listenSessionDropped} from './state/events' import {useColorMode} from 'state/shell' @@ -25,6 +27,7 @@ import {queryClient} from 'lib/react-query' import {TestCtrls} from 'view/com/testing/TestCtrls' import {Provider as ShellStateProvider} from 'state/shell' import {Provider as ModalStateProvider} from 'state/modals' +import {Provider as DialogStateProvider} from 'state/dialogs' import {Provider as LightboxStateProvider} from 'state/lightbox' import {Provider as MutedThreadsProvider} from 'state/muted-threads' import {Provider as InvitesStateProvider} from 'state/invites' @@ -39,6 +42,7 @@ import { import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread' import * as persisted from '#/state/persisted' import {Splash} from '#/Splash' +import {Provider as PortalProvider} from '#/components/Portal' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -48,6 +52,7 @@ function InnerApp() { const colorMode = useColorMode() const {isInitialLoad, currentAccount} = useSession() const {resumeSession} = useSessionApi() + const theme = useColorModeTheme(colorMode) const {_} = useLingui() // init @@ -63,25 +68,27 @@ function InnerApp() { return ( - - - - - - {/* All components should be within this provider */} - - - - - - - - - - - + + + + + + + {/* All components should be within this provider */} + + + + + + + + + + + + ) } @@ -109,11 +116,15 @@ function App() { - - - - - + + + + + + + + + diff --git a/src/App.web.tsx b/src/App.web.tsx index 1bdb3c208c..1efa0567c7 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -8,6 +8,7 @@ import {RootSiblingParent} from 'react-native-root-siblings' import 'view/icons' import {ThemeProvider as Alf} from '#/alf' +import {useColorModeTheme} from '#/alf/util/useColorModeTheme' import {init as initPersistedState} from '#/state/persisted' import {useColorMode} from 'state/shell' import {Shell} from 'view/shell/index' @@ -16,6 +17,7 @@ import {ThemeProvider} from 'lib/ThemeContext' import {queryClient} from 'lib/react-query' import {Provider as ShellStateProvider} from 'state/shell' import {Provider as ModalStateProvider} from 'state/modals' +import {Provider as DialogStateProvider} from 'state/dialogs' import {Provider as LightboxStateProvider} from 'state/lightbox' import {Provider as MutedThreadsProvider} from 'state/muted-threads' import {Provider as InvitesStateProvider} from 'state/invites' @@ -29,7 +31,7 @@ import { } from 'state/session' import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread' import * as persisted from '#/state/persisted' -import {useColorModeTheme} from '#/alf/util/useColorModeTheme' +import {Provider as PortalProvider} from '#/components/Portal' function InnerApp() { const {isInitialLoad, currentAccount} = useSession() @@ -92,11 +94,15 @@ function App() { - - - - - + + + + + + + + + diff --git a/src/Navigation.tsx b/src/Navigation.tsx index c68cb05807..08cdd1d893 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -61,7 +61,7 @@ import {ProfileListScreen} from './view/screens/ProfileList' import {PostThreadScreen} from './view/screens/PostThread' import {PostLikedByScreen} from './view/screens/PostLikedBy' import {PostRepostedByScreen} from './view/screens/PostRepostedBy' -import {DebugScreen} from './view/screens/DebugNew' +import {Storybook} from './view/screens/Storybook' import {LogScreen} from './view/screens/Log' import {SupportScreen} from './view/screens/Support' import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy' @@ -144,7 +144,7 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { name="Profile" getComponent={() => ProfileScreen} options={({route}) => ({ - title: title(msg`@${route.params.name}`), + title: bskyTitle(`@${route.params.name}`, unreadCountLabel), animation: 'none', })} /> @@ -200,8 +200,8 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { /> DebugScreen} - options={{title: title(msg`Debug`), requireAuth: true}} + getComponent={() => Storybook} + options={{title: title(msg`Storybook`), requireAuth: true}} /> +export type ReadonlyPalette = typeof lightPalette +export type Palette = Mutable -export type Palette = { - primary: string - positive: string - negative: string -} +export const lightPalette = { + white: tokens.color.gray_0, + black: tokens.color.gray_1000, -export const lightPalette: Palette = { - primary: tokens.color.blue_500, - positive: tokens.color.green_500, - negative: tokens.color.red_500, + contrast_25: tokens.color.gray_25, + contrast_50: tokens.color.gray_50, + contrast_100: tokens.color.gray_100, + contrast_200: tokens.color.gray_200, + contrast_300: tokens.color.gray_300, + contrast_400: tokens.color.gray_400, + contrast_500: tokens.color.gray_500, + contrast_600: tokens.color.gray_600, + contrast_700: tokens.color.gray_700, + contrast_800: tokens.color.gray_800, + contrast_900: tokens.color.gray_900, + contrast_950: tokens.color.gray_950, + contrast_975: tokens.color.gray_975, + + primary_25: tokens.color.blue_25, + primary_50: tokens.color.blue_50, + primary_100: tokens.color.blue_100, + primary_200: tokens.color.blue_200, + primary_300: tokens.color.blue_300, + primary_400: tokens.color.blue_400, + primary_500: tokens.color.blue_500, + primary_600: tokens.color.blue_600, + primary_700: tokens.color.blue_700, + primary_800: tokens.color.blue_800, + primary_900: tokens.color.blue_900, + primary_950: tokens.color.blue_950, + primary_975: tokens.color.blue_975, + + positive_25: tokens.color.green_25, + positive_50: tokens.color.green_50, + positive_100: tokens.color.green_100, + positive_200: tokens.color.green_200, + positive_300: tokens.color.green_300, + positive_400: tokens.color.green_400, + positive_500: tokens.color.green_500, + positive_600: tokens.color.green_600, + positive_700: tokens.color.green_700, + positive_800: tokens.color.green_800, + positive_900: tokens.color.green_900, + positive_950: tokens.color.green_950, + positive_975: tokens.color.green_975, + + negative_25: tokens.color.red_25, + negative_50: tokens.color.red_50, + negative_100: tokens.color.red_100, + negative_200: tokens.color.red_200, + negative_300: tokens.color.red_300, + negative_400: tokens.color.red_400, + negative_500: tokens.color.red_500, + negative_600: tokens.color.red_600, + negative_700: tokens.color.red_700, + negative_800: tokens.color.red_800, + negative_900: tokens.color.red_900, + negative_950: tokens.color.red_950, + negative_975: tokens.color.red_975, } as const export const darkPalette: Palette = { - primary: tokens.color.blue_500, - positive: tokens.color.green_400, - negative: tokens.color.red_400, + white: tokens.color.gray_0, + black: tokens.color.gray_1000, + + contrast_25: tokens.color.gray_975, + contrast_50: tokens.color.gray_950, + contrast_100: tokens.color.gray_900, + contrast_200: tokens.color.gray_800, + contrast_300: tokens.color.gray_700, + contrast_400: tokens.color.gray_600, + contrast_500: tokens.color.gray_500, + contrast_600: tokens.color.gray_400, + contrast_700: tokens.color.gray_300, + contrast_800: tokens.color.gray_200, + contrast_900: tokens.color.gray_100, + contrast_950: tokens.color.gray_50, + contrast_975: tokens.color.gray_25, + + primary_25: tokens.color.blue_25, + primary_50: tokens.color.blue_50, + primary_100: tokens.color.blue_100, + primary_200: tokens.color.blue_200, + primary_300: tokens.color.blue_300, + primary_400: tokens.color.blue_400, + primary_500: tokens.color.blue_500, + primary_600: tokens.color.blue_600, + primary_700: tokens.color.blue_700, + primary_800: tokens.color.blue_800, + primary_900: tokens.color.blue_900, + primary_950: tokens.color.blue_950, + primary_975: tokens.color.blue_975, + + positive_25: tokens.color.green_25, + positive_50: tokens.color.green_50, + positive_100: tokens.color.green_100, + positive_200: tokens.color.green_200, + positive_300: tokens.color.green_300, + positive_400: tokens.color.green_400, + positive_500: tokens.color.green_500, + positive_600: tokens.color.green_600, + positive_700: tokens.color.green_700, + positive_800: tokens.color.green_800, + positive_900: tokens.color.green_900, + positive_950: tokens.color.green_950, + positive_975: tokens.color.green_975, + + negative_25: tokens.color.red_25, + negative_50: tokens.color.red_50, + negative_100: tokens.color.red_100, + negative_200: tokens.color.red_200, + negative_300: tokens.color.red_300, + negative_400: tokens.color.red_400, + negative_500: tokens.color.red_500, + negative_600: tokens.color.red_600, + negative_700: tokens.color.red_700, + negative_800: tokens.color.red_800, + negative_900: tokens.color.red_900, + negative_950: tokens.color.red_950, + negative_975: tokens.color.red_975, } as const export const light = { + name: 'light', palette: lightPalette, atoms: { text: { - color: tokens.color.gray_1000, + color: lightPalette.black, }, text_contrast_700: { - color: tokens.color.gray_700, + color: lightPalette.contrast_700, + }, + text_contrast_600: { + color: lightPalette.contrast_600, }, text_contrast_500: { - color: tokens.color.gray_500, + color: lightPalette.contrast_500, + }, + text_contrast_400: { + color: lightPalette.contrast_400, }, text_inverted: { - color: tokens.color.white, + color: lightPalette.white, }, bg: { - backgroundColor: tokens.color.white, + backgroundColor: lightPalette.white, + }, + bg_contrast_25: { + backgroundColor: lightPalette.contrast_25, + }, + bg_contrast_50: { + backgroundColor: lightPalette.contrast_50, }, bg_contrast_100: { - backgroundColor: tokens.color.gray_100, + backgroundColor: lightPalette.contrast_100, }, bg_contrast_200: { - backgroundColor: tokens.color.gray_200, + backgroundColor: lightPalette.contrast_200, }, bg_contrast_300: { - backgroundColor: tokens.color.gray_300, - }, - bg_positive: { - backgroundColor: tokens.color.green_500, - }, - bg_negative: { - backgroundColor: tokens.color.red_400, + backgroundColor: lightPalette.contrast_300, }, border: { - borderColor: tokens.color.gray_200, + borderColor: lightPalette.contrast_100, }, - border_contrast_500: { - borderColor: tokens.color.gray_500, + border_contrast: { + borderColor: lightPalette.contrast_400, + }, + shadow_sm: { + ...atoms.shadow_sm, + shadowColor: lightPalette.black, + }, + shadow_md: { + ...atoms.shadow_md, + shadowColor: lightPalette.black, + }, + shadow_lg: { + ...atoms.shadow_lg, + shadowColor: lightPalette.black, + }, + }, +} + +export const dim: Theme = { + name: 'dim', + palette: darkPalette, + atoms: { + text: { + color: darkPalette.white, + }, + text_contrast_700: { + color: darkPalette.contrast_800, + }, + text_contrast_600: { + color: darkPalette.contrast_700, + }, + text_contrast_500: { + color: darkPalette.contrast_600, + }, + text_contrast_400: { + color: darkPalette.contrast_500, + }, + text_inverted: { + color: darkPalette.black, + }, + bg: { + backgroundColor: darkPalette.contrast_50, + }, + bg_contrast_25: { + backgroundColor: darkPalette.contrast_100, + }, + bg_contrast_50: { + backgroundColor: darkPalette.contrast_200, + }, + bg_contrast_100: { + backgroundColor: darkPalette.contrast_300, + }, + bg_contrast_200: { + backgroundColor: darkPalette.contrast_400, + }, + bg_contrast_300: { + backgroundColor: darkPalette.contrast_500, + }, + border: { + borderColor: darkPalette.contrast_200, + }, + border_contrast: { + borderColor: darkPalette.contrast_400, + }, + shadow_sm: { + ...atoms.shadow_sm, + shadowOpacity: 0.7, + shadowColor: tokens.color.trueBlack, + }, + shadow_md: { + ...atoms.shadow_md, + shadowOpacity: 0.7, + shadowColor: tokens.color.trueBlack, + }, + shadow_lg: { + ...atoms.shadow_lg, + shadowOpacity: 0.7, + shadowColor: tokens.color.trueBlack, }, }, } export const dark: Theme = { + name: 'dark', palette: darkPalette, atoms: { text: { - color: tokens.color.white, + color: darkPalette.white, }, text_contrast_700: { - color: tokens.color.gray_300, + color: darkPalette.contrast_700, + }, + text_contrast_600: { + color: darkPalette.contrast_600, }, text_contrast_500: { - color: tokens.color.gray_500, + color: darkPalette.contrast_500, + }, + text_contrast_400: { + color: darkPalette.contrast_400, }, text_inverted: { - color: tokens.color.gray_1000, + color: darkPalette.black, }, bg: { - backgroundColor: tokens.color.gray_1000, + backgroundColor: darkPalette.black, + }, + bg_contrast_25: { + backgroundColor: darkPalette.contrast_50, + }, + bg_contrast_50: { + backgroundColor: darkPalette.contrast_100, }, bg_contrast_100: { - backgroundColor: tokens.color.gray_900, + backgroundColor: darkPalette.contrast_200, }, bg_contrast_200: { - backgroundColor: tokens.color.gray_800, + backgroundColor: darkPalette.contrast_300, }, bg_contrast_300: { - backgroundColor: tokens.color.gray_700, - }, - bg_positive: { - backgroundColor: tokens.color.green_400, - }, - bg_negative: { - backgroundColor: tokens.color.red_400, + backgroundColor: darkPalette.contrast_400, }, border: { - borderColor: tokens.color.gray_800, + borderColor: darkPalette.contrast_100, }, - border_contrast_500: { - borderColor: tokens.color.gray_500, + border_contrast: { + borderColor: darkPalette.contrast_300, + }, + shadow_sm: { + ...atoms.shadow_sm, + shadowOpacity: 0.7, + shadowColor: tokens.color.trueBlack, + }, + shadow_md: { + ...atoms.shadow_md, + shadowOpacity: 0.7, + shadowColor: tokens.color.trueBlack, + }, + shadow_lg: { + ...atoms.shadow_lg, + shadowOpacity: 0.7, + shadowColor: tokens.color.trueBlack, }, }, } diff --git a/src/alf/tokens.ts b/src/alf/tokens.ts index 4034e0deb4..0e370cdc17 100644 --- a/src/alf/tokens.ts +++ b/src/alf/tokens.ts @@ -1,79 +1,95 @@ const BLUE_HUE = 211 -const GRAYSCALE_SATURATION = 22 +const RED_HUE = 346 +const GREEN_HUE = 152 export const color = { - white: '#FFFFFF', + trueBlack: '#000000', - gray_0: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 100%)`, - gray_100: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 95%)`, - gray_200: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 85%)`, - gray_300: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 75%)`, - gray_400: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 65%)`, - gray_500: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 55%)`, - gray_600: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 45%)`, - gray_700: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 35%)`, - gray_800: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 25%)`, - gray_900: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 15%)`, - gray_1000: `hsl(${BLUE_HUE}, ${GRAYSCALE_SATURATION}%, 5%)`, + gray_0: `hsl(${BLUE_HUE}, 20%, 100%)`, + gray_25: `hsl(${BLUE_HUE}, 20%, 97%)`, + gray_50: `hsl(${BLUE_HUE}, 20%, 95%)`, + gray_100: `hsl(${BLUE_HUE}, 20%, 90%)`, + gray_200: `hsl(${BLUE_HUE}, 20%, 80%)`, + gray_300: `hsl(${BLUE_HUE}, 20%, 70%)`, + gray_400: `hsl(${BLUE_HUE}, 20%, 60%)`, + gray_500: `hsl(${BLUE_HUE}, 20%, 50%)`, + gray_600: `hsl(${BLUE_HUE}, 20%, 42%)`, + gray_700: `hsl(${BLUE_HUE}, 20%, 34%)`, + gray_800: `hsl(${BLUE_HUE}, 20%, 26%)`, + gray_900: `hsl(${BLUE_HUE}, 20%, 18%)`, + gray_950: `hsl(${BLUE_HUE}, 20%, 10%)`, + gray_975: `hsl(${BLUE_HUE}, 20%, 7%)`, + gray_1000: `hsl(${BLUE_HUE}, 20%, 4%)`, - blue_0: `hsl(${BLUE_HUE}, 99%, 100%)`, - blue_100: `hsl(${BLUE_HUE}, 99%, 93%)`, - blue_200: `hsl(${BLUE_HUE}, 99%, 83%)`, - blue_300: `hsl(${BLUE_HUE}, 99%, 73%)`, - blue_400: `hsl(${BLUE_HUE}, 99%, 63%)`, + blue_25: `hsl(${BLUE_HUE}, 99%, 97%)`, + blue_50: `hsl(${BLUE_HUE}, 99%, 95%)`, + blue_100: `hsl(${BLUE_HUE}, 99%, 90%)`, + blue_200: `hsl(${BLUE_HUE}, 99%, 80%)`, + blue_300: `hsl(${BLUE_HUE}, 99%, 70%)`, + blue_400: `hsl(${BLUE_HUE}, 99%, 60%)`, blue_500: `hsl(${BLUE_HUE}, 99%, 53%)`, - blue_600: `hsl(${BLUE_HUE}, 99%, 43%)`, - blue_700: `hsl(${BLUE_HUE}, 99%, 33%)`, - blue_800: `hsl(${BLUE_HUE}, 99%, 23%)`, - blue_900: `hsl(${BLUE_HUE}, 99%, 13%)`, - blue_1000: `hsl(${BLUE_HUE}, 99%, 8%)`, + blue_600: `hsl(${BLUE_HUE}, 99%, 42%)`, + blue_700: `hsl(${BLUE_HUE}, 99%, 34%)`, + blue_800: `hsl(${BLUE_HUE}, 99%, 26%)`, + blue_900: `hsl(${BLUE_HUE}, 99%, 18%)`, + blue_950: `hsl(${BLUE_HUE}, 99%, 10%)`, + blue_975: `hsl(${BLUE_HUE}, 99%, 7%)`, - green_0: `hsl(130, 60%, 100%)`, - green_100: `hsl(130, 60%, 95%)`, - green_200: `hsl(130, 60%, 85%)`, - green_300: `hsl(130, 60%, 75%)`, - green_400: `hsl(130, 60%, 65%)`, - green_500: `hsl(130, 60%, 55%)`, - green_600: `hsl(130, 60%, 45%)`, - green_700: `hsl(130, 60%, 35%)`, - green_800: `hsl(130, 60%, 25%)`, - green_900: `hsl(130, 60%, 15%)`, - green_1000: `hsl(130, 60%, 5%)`, + green_25: `hsl(${GREEN_HUE}, 82%, 97%)`, + green_50: `hsl(${GREEN_HUE}, 82%, 95%)`, + green_100: `hsl(${GREEN_HUE}, 82%, 90%)`, + green_200: `hsl(${GREEN_HUE}, 82%, 80%)`, + green_300: `hsl(${GREEN_HUE}, 82%, 70%)`, + green_400: `hsl(${GREEN_HUE}, 82%, 60%)`, + green_500: `hsl(${GREEN_HUE}, 82%, 50%)`, + green_600: `hsl(${GREEN_HUE}, 82%, 42%)`, + green_700: `hsl(${GREEN_HUE}, 82%, 34%)`, + green_800: `hsl(${GREEN_HUE}, 82%, 26%)`, + green_900: `hsl(${GREEN_HUE}, 82%, 18%)`, + green_950: `hsl(${GREEN_HUE}, 82%, 10%)`, + green_975: `hsl(${GREEN_HUE}, 82%, 7%)`, - red_0: `hsl(349, 96%, 100%)`, - red_100: `hsl(349, 96%, 95%)`, - red_200: `hsl(349, 96%, 85%)`, - red_300: `hsl(349, 96%, 75%)`, - red_400: `hsl(349, 96%, 65%)`, - red_500: `hsl(349, 96%, 55%)`, - red_600: `hsl(349, 96%, 45%)`, - red_700: `hsl(349, 96%, 35%)`, - red_800: `hsl(349, 96%, 25%)`, - red_900: `hsl(349, 96%, 15%)`, - red_1000: `hsl(349, 96%, 5%)`, + red_25: `hsl(${RED_HUE}, 91%, 97%)`, + red_50: `hsl(${RED_HUE}, 91%, 95%)`, + red_100: `hsl(${RED_HUE}, 91%, 90%)`, + red_200: `hsl(${RED_HUE}, 91%, 80%)`, + red_300: `hsl(${RED_HUE}, 91%, 70%)`, + red_400: `hsl(${RED_HUE}, 91%, 60%)`, + red_500: `hsl(${RED_HUE}, 91%, 50%)`, + red_600: `hsl(${RED_HUE}, 91%, 42%)`, + red_700: `hsl(${RED_HUE}, 91%, 34%)`, + red_800: `hsl(${RED_HUE}, 91%, 26%)`, + red_900: `hsl(${RED_HUE}, 91%, 18%)`, + red_950: `hsl(${RED_HUE}, 91%, 10%)`, + red_975: `hsl(${RED_HUE}, 91%, 7%)`, } as const export const space = { - xxs: 2, + _2xs: 2, xs: 4, sm: 8, md: 12, - lg: 18, - xl: 24, - xxl: 32, + lg: 16, + xl: 20, + _2xl: 24, + _3xl: 28, + _4xl: 32, + _5xl: 40, } as const export const fontSize = { - xxs: 10, + _2xs: 10, xs: 12, sm: 14, md: 16, lg: 18, - xl: 22, - xxl: 26, + xl: 20, + _2xl: 22, + _3xl: 26, + _4xl: 32, + _5xl: 40, } as const -// TODO test export const lineHeight = { none: 1, normal: 1.5, @@ -81,6 +97,8 @@ export const lineHeight = { } as const export const borderRadius = { + _2xs: 2, + xs: 4, sm: 8, md: 12, full: 999, @@ -92,6 +110,56 @@ export const fontWeight = { bold: '900', } as const +export const gradients = { + sky: { + values: [ + [0, '#0A7AFF'], + [1, '#59B9FF'], + ], + hover_value: '#0A7AFF', + }, + midnight: { + values: [ + [0, '#022C5E'], + [1, '#4079BC'], + ], + hover_value: '#022C5E', + }, + sunrise: { + values: [ + [0, '#4E90AE'], + [0.4, '#AEA3AB'], + [0.8, '#E6A98F'], + [1, '#F3A84C'], + ], + hover_value: '#AEA3AB', + }, + sunset: { + values: [ + [0, '#6772AF'], + [0.6, '#B88BB6'], + [1, '#FFA6AC'], + ], + hover_value: '#B88BB6', + }, + nordic: { + values: [ + [0, '#083367'], + [1, '#9EE8C1'], + ], + hover_value: '#3A7085', + }, + bonfire: { + values: [ + [0, '#203E4E'], + [0.4, '#755B62'], + [0.8, '#CD7765'], + [1, '#EF956E'], + ], + hover_value: '#755B62', + }, +} as const + export type Color = keyof typeof color export type Space = keyof typeof space export type FontSize = keyof typeof fontSize diff --git a/src/alf/util/flatten.ts b/src/alf/util/flatten.ts new file mode 100644 index 0000000000..448716a082 --- /dev/null +++ b/src/alf/util/flatten.ts @@ -0,0 +1,3 @@ +import {StyleSheet} from 'react-native' + +export const flatten = StyleSheet.flatten diff --git a/src/components/Button.tsx b/src/components/Button.tsx new file mode 100644 index 0000000000..d2100f0b42 --- /dev/null +++ b/src/components/Button.tsx @@ -0,0 +1,507 @@ +import React from 'react' +import { + Pressable, + Text, + PressableProps, + TextProps, + ViewStyle, + AccessibilityProps, + View, + TextStyle, + StyleSheet, +} from 'react-native' +import LinearGradient from 'react-native-linear-gradient' + +import {useTheme, atoms as a, tokens, web, native} from '#/alf' +import {Props as SVGIconProps} from '#/components/icons/common' + +export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'gradient' +export type ButtonColor = + | 'primary' + | 'secondary' + | 'negative' + | 'gradient_sky' + | 'gradient_midnight' + | 'gradient_sunrise' + | 'gradient_sunset' + | 'gradient_nordic' + | 'gradient_bonfire' +export type ButtonSize = 'small' | 'large' +export type VariantProps = { + /** + * The style variation of the button + */ + variant?: ButtonVariant + /** + * The color of the button + */ + color?: ButtonColor + /** + * The size of the button + */ + size?: ButtonSize +} + +export type ButtonProps = React.PropsWithChildren< + Pick & + AccessibilityProps & + VariantProps & { + label: string + } +> +export type ButtonTextProps = TextProps & VariantProps & {disabled?: boolean} + +const Context = React.createContext< + VariantProps & { + hovered: boolean + focused: boolean + pressed: boolean + disabled: boolean + } +>({ + hovered: false, + focused: false, + pressed: false, + disabled: false, +}) + +export function useButtonContext() { + return React.useContext(Context) +} + +export function Button({ + children, + variant, + color, + size, + label, + disabled = false, + ...rest +}: ButtonProps) { + const t = useTheme() + const [state, setState] = React.useState({ + pressed: false, + hovered: false, + focused: false, + }) + + const onPressIn = React.useCallback(() => { + setState(s => ({ + ...s, + pressed: true, + })) + }, [setState]) + const onPressOut = React.useCallback(() => { + setState(s => ({ + ...s, + pressed: false, + })) + }, [setState]) + const onHoverIn = React.useCallback(() => { + setState(s => ({ + ...s, + hovered: true, + })) + }, [setState]) + const onHoverOut = React.useCallback(() => { + setState(s => ({ + ...s, + hovered: false, + })) + }, [setState]) + const onFocus = React.useCallback(() => { + setState(s => ({ + ...s, + focused: true, + })) + }, [setState]) + const onBlur = React.useCallback(() => { + setState(s => ({ + ...s, + focused: false, + })) + }, [setState]) + + const {baseStyles, hoverStyles, focusStyles} = React.useMemo(() => { + const baseStyles: ViewStyle[] = [] + const hoverStyles: ViewStyle[] = [] + const light = t.name === 'light' + + if (color === 'primary') { + if (variant === 'solid') { + if (!disabled) { + baseStyles.push({ + backgroundColor: t.palette.primary_500, + }) + hoverStyles.push({ + backgroundColor: t.palette.primary_600, + }) + } else { + baseStyles.push({ + backgroundColor: t.palette.primary_700, + }) + } + } else if (variant === 'outline') { + baseStyles.push(a.border, t.atoms.bg, { + borderWidth: 1, + }) + + if (!disabled) { + baseStyles.push(a.border, { + borderColor: tokens.color.blue_500, + }) + hoverStyles.push(a.border, { + backgroundColor: light + ? t.palette.primary_50 + : t.palette.primary_950, + }) + } else { + baseStyles.push(a.border, { + borderColor: light ? tokens.color.blue_200 : tokens.color.blue_900, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push(t.atoms.bg) + hoverStyles.push({ + backgroundColor: light + ? t.palette.primary_100 + : t.palette.primary_900, + }) + } + } + } else if (color === 'secondary') { + if (variant === 'solid') { + if (!disabled) { + baseStyles.push({ + backgroundColor: light + ? tokens.color.gray_100 + : tokens.color.gray_900, + }) + hoverStyles.push({ + backgroundColor: light + ? tokens.color.gray_200 + : tokens.color.gray_950, + }) + } else { + baseStyles.push({ + backgroundColor: light + ? tokens.color.gray_300 + : tokens.color.gray_950, + }) + } + } else if (variant === 'outline') { + baseStyles.push(a.border, t.atoms.bg, { + borderWidth: 1, + }) + + if (!disabled) { + baseStyles.push(a.border, { + borderColor: light ? tokens.color.gray_500 : tokens.color.gray_500, + }) + hoverStyles.push(a.border, t.atoms.bg_contrast_50) + } else { + baseStyles.push(a.border, { + borderColor: light ? tokens.color.gray_200 : tokens.color.gray_800, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push(t.atoms.bg) + hoverStyles.push({ + backgroundColor: light + ? tokens.color.gray_100 + : tokens.color.gray_900, + }) + } + } + } else if (color === 'negative') { + if (variant === 'solid') { + if (!disabled) { + baseStyles.push({ + backgroundColor: t.palette.negative_400, + }) + hoverStyles.push({ + backgroundColor: t.palette.negative_500, + }) + } else { + baseStyles.push({ + backgroundColor: t.palette.negative_600, + }) + } + } else if (variant === 'outline') { + baseStyles.push(a.border, t.atoms.bg, { + borderWidth: 1, + }) + + if (!disabled) { + baseStyles.push(a.border, { + borderColor: t.palette.negative_400, + }) + hoverStyles.push(a.border, { + backgroundColor: light + ? t.palette.negative_50 + : t.palette.negative_975, + }) + } else { + baseStyles.push(a.border, { + borderColor: light + ? t.palette.negative_200 + : t.palette.negative_900, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push(t.atoms.bg) + hoverStyles.push({ + backgroundColor: light + ? t.palette.negative_100 + : t.palette.negative_950, + }) + } + } + } + + if (size === 'large') { + baseStyles.push({paddingVertical: 15}, a.px_2xl, a.rounded_sm, a.gap_sm) + } else if (size === 'small') { + baseStyles.push({paddingVertical: 9}, a.px_md, a.rounded_sm, a.gap_sm) + } + + return { + baseStyles, + hoverStyles, + focusStyles: [ + ...hoverStyles, + { + outline: 0, + } as ViewStyle, + ], + } + }, [t, variant, color, size, disabled]) + + const {gradientColors, gradientHoverColors, gradientLocations} = + React.useMemo(() => { + const colors: string[] = [] + const hoverColors: string[] = [] + const locations: number[] = [] + const gradient = { + primary: tokens.gradients.sky, + secondary: tokens.gradients.sky, + negative: tokens.gradients.sky, + gradient_sky: tokens.gradients.sky, + gradient_midnight: tokens.gradients.midnight, + gradient_sunrise: tokens.gradients.sunrise, + gradient_sunset: tokens.gradients.sunset, + gradient_nordic: tokens.gradients.nordic, + gradient_bonfire: tokens.gradients.bonfire, + }[color || 'primary'] + + if (variant === 'gradient') { + colors.push(...gradient.values.map(([_, color]) => color)) + hoverColors.push(...gradient.values.map(_ => gradient.hover_value)) + locations.push(...gradient.values.map(([location, _]) => location)) + } + + return { + gradientColors: colors, + gradientHoverColors: hoverColors, + gradientLocations: locations, + } + }, [variant, color]) + + const context = React.useMemo( + () => ({ + ...state, + variant, + color, + size, + disabled: disabled || false, + }), + [state, variant, color, size, disabled], + ) + + return ( + + {variant === 'gradient' && ( + + )} + + {typeof children === 'string' ? ( + {children} + ) : ( + children + )} + + + ) +} + +export function useSharedButtonTextStyles() { + const t = useTheme() + const {color, variant, disabled, size} = useButtonContext() + return React.useMemo(() => { + const baseStyles: TextStyle[] = [] + const light = t.name === 'light' + + if (color === 'primary') { + if (variant === 'solid') { + if (!disabled) { + baseStyles.push({color: t.palette.white}) + } else { + baseStyles.push({color: t.palette.white, opacity: 0.5}) + } + } else if (variant === 'outline') { + if (!disabled) { + baseStyles.push({ + color: light ? t.palette.primary_600 : t.palette.primary_500, + }) + } else { + baseStyles.push({color: t.palette.primary_600, opacity: 0.5}) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push({color: t.palette.primary_600}) + } else { + baseStyles.push({color: t.palette.primary_600, opacity: 0.5}) + } + } + } else if (color === 'secondary') { + if (variant === 'solid' || variant === 'gradient') { + if (!disabled) { + baseStyles.push({ + color: light ? tokens.color.gray_700 : tokens.color.gray_100, + }) + } else { + baseStyles.push({ + color: light ? tokens.color.gray_400 : tokens.color.gray_700, + }) + } + } else if (variant === 'outline') { + if (!disabled) { + baseStyles.push({ + color: light ? tokens.color.gray_600 : tokens.color.gray_300, + }) + } else { + baseStyles.push({ + color: light ? tokens.color.gray_400 : tokens.color.gray_700, + }) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push({ + color: light ? tokens.color.gray_600 : tokens.color.gray_300, + }) + } else { + baseStyles.push({ + color: light ? tokens.color.gray_400 : tokens.color.gray_600, + }) + } + } + } else if (color === 'negative') { + if (variant === 'solid' || variant === 'gradient') { + if (!disabled) { + baseStyles.push({color: t.palette.white}) + } else { + baseStyles.push({color: t.palette.white, opacity: 0.5}) + } + } else if (variant === 'outline') { + if (!disabled) { + baseStyles.push({color: t.palette.negative_400}) + } else { + baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) + } + } else if (variant === 'ghost') { + if (!disabled) { + baseStyles.push({color: t.palette.negative_400}) + } else { + baseStyles.push({color: t.palette.negative_400, opacity: 0.5}) + } + } + } else { + if (!disabled) { + baseStyles.push({color: t.palette.white}) + } else { + baseStyles.push({color: t.palette.white, opacity: 0.5}) + } + } + + if (size === 'large') { + baseStyles.push( + a.text_md, + web({paddingBottom: 1}), + native({marginTop: 2}), + ) + } else { + baseStyles.push( + a.text_md, + web({paddingBottom: 1}), + native({marginTop: 2}), + ) + } + + return StyleSheet.flatten(baseStyles) + }, [t, variant, color, size, disabled]) +} + +export function ButtonText({children, style, ...rest}: ButtonTextProps) { + const textStyles = useSharedButtonTextStyles() + + return ( + + {children} + + ) +} + +export function ButtonIcon({ + icon: Comp, +}: { + icon: React.ComponentType +}) { + const {size} = useButtonContext() + const textStyles = useSharedButtonTextStyles() + + return ( + + + + ) +} diff --git a/src/components/Dialog/context.ts b/src/components/Dialog/context.ts new file mode 100644 index 0000000000..b28b9f5a25 --- /dev/null +++ b/src/components/Dialog/context.ts @@ -0,0 +1,35 @@ +import React from 'react' + +import {useDialogStateContext} from '#/state/dialogs' +import {DialogContextProps, DialogControlProps} from '#/components/Dialog/types' + +export const Context = React.createContext({ + close: () => {}, +}) + +export function useDialogContext() { + return React.useContext(Context) +} + +export function useDialogControl() { + const id = React.useId() + const control = React.useRef({ + open: () => {}, + close: () => {}, + }) + const {activeDialogs} = useDialogStateContext() + + React.useEffect(() => { + activeDialogs.current.set(id, control) + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + activeDialogs.current.delete(id) + } + }, [id, activeDialogs]) + + return { + ref: control, + open: () => control.current.open(), + close: () => control.current.close(), + } +} diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx new file mode 100644 index 0000000000..44e4dc8a70 --- /dev/null +++ b/src/components/Dialog/index.tsx @@ -0,0 +1,162 @@ +import React, {useImperativeHandle} from 'react' +import {View, Dimensions} from 'react-native' +import BottomSheet, { + BottomSheetBackdrop, + BottomSheetScrollView, + BottomSheetTextInput, + BottomSheetView, +} from '@gorhom/bottom-sheet' +import {useSafeAreaInsets} from 'react-native-safe-area-context' + +import {useTheme, atoms as a} from '#/alf' +import {Portal} from '#/components/Portal' +import {createInput} from '#/components/forms/TextField' + +import { + DialogOuterProps, + DialogControlProps, + DialogInnerProps, +} from '#/components/Dialog/types' +import {Context} from '#/components/Dialog/context' + +export {useDialogControl, useDialogContext} from '#/components/Dialog/context' +export * from '#/components/Dialog/types' +// @ts-ignore +export const Input = createInput(BottomSheetTextInput) + +export function Outer({ + children, + control, + onClose, + nativeOptions, +}: React.PropsWithChildren) { + const t = useTheme() + const sheet = React.useRef(null) + const sheetOptions = nativeOptions?.sheet || {} + const hasSnapPoints = !!sheetOptions.snapPoints + + const open = React.useCallback((i = 0) => { + sheet.current?.snapToIndex(i) + }, []) + + const close = React.useCallback(() => { + sheet.current?.close() + onClose?.() + }, [onClose]) + + useImperativeHandle( + control.ref, + () => ({ + open, + close, + }), + [open, close], + ) + + const context = React.useMemo(() => ({close}), [close]) + + return ( + + ( + + )} + handleIndicatorStyle={{backgroundColor: t.palette.primary_500}} + handleStyle={{display: 'none'}} + onClose={onClose}> + + + {children} + + + + ) +} + +// TODO a11y props here, or is that handled by the sheet? +export function Inner(props: DialogInnerProps) { + const insets = useSafeAreaInsets() + return ( + + {props.children} + + ) +} + +export function ScrollableInner(props: DialogInnerProps) { + const insets = useSafeAreaInsets() + return ( + + {props.children} + + + ) +} + +export function Handle() { + const t = useTheme() + return ( + + ) +} + +export function Close() { + return null +} diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx new file mode 100644 index 0000000000..305c00e97a --- /dev/null +++ b/src/components/Dialog/index.web.tsx @@ -0,0 +1,194 @@ +import React, {useImperativeHandle} from 'react' +import {View, TouchableWithoutFeedback} from 'react-native' +import {FocusScope} from '@tamagui/focus-scope' +import Animated, {FadeInDown, FadeIn} from 'react-native-reanimated' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useTheme, atoms as a, useBreakpoints, web} from '#/alf' +import {Portal} from '#/components/Portal' + +import {DialogOuterProps, DialogInnerProps} from '#/components/Dialog/types' +import {Context} from '#/components/Dialog/context' + +export {useDialogControl, useDialogContext} from '#/components/Dialog/context' +export * from '#/components/Dialog/types' +export {Input} from '#/components/forms/TextField' + +const stopPropagation = (e: any) => e.stopPropagation() + +export function Outer({ + control, + onClose, + children, +}: React.PropsWithChildren) { + const {_} = useLingui() + const t = useTheme() + const {gtMobile} = useBreakpoints() + const [isOpen, setIsOpen] = React.useState(false) + const [isVisible, setIsVisible] = React.useState(true) + + const open = React.useCallback(() => { + setIsOpen(true) + }, [setIsOpen]) + + const close = React.useCallback(async () => { + setIsVisible(false) + await new Promise(resolve => setTimeout(resolve, 150)) + setIsOpen(false) + setIsVisible(true) + onClose?.() + }, [onClose, setIsOpen]) + + useImperativeHandle( + control.ref, + () => ({ + open, + close, + }), + [open, close], + ) + + React.useEffect(() => { + if (!isOpen) return + + function handler(e: KeyboardEvent) { + if (e.key === 'Escape') close() + } + + document.addEventListener('keydown', handler) + + return () => document.removeEventListener('keydown', handler) + }, [isOpen, close]) + + const context = React.useMemo( + () => ({ + close, + }), + [close], + ) + + return ( + <> + {isOpen && ( + + + + + {isVisible && ( + + )} + + + {isVisible ? children : null} + + + + + + )} + + ) +} + +export function Inner({ + children, + style, + label, + accessibilityLabelledBy, + accessibilityDescribedBy, +}: DialogInnerProps) { + const t = useTheme() + const {gtMobile} = useBreakpoints() + return ( + + true} + onTouchEnd={stopPropagation} + entering={FadeInDown.duration(100)} + // exiting={FadeOut.duration(100)} + style={[ + a.relative, + a.rounded_md, + a.w_full, + a.border, + gtMobile ? a.p_xl : a.p_lg, + t.atoms.bg, + { + maxWidth: 600, + borderColor: t.palette.contrast_200, + shadowColor: t.palette.black, + shadowOpacity: t.name === 'light' ? 0.1 : 0.4, + shadowRadius: 30, + }, + ...(Array.isArray(style) ? style : [style || {}]), + ]}> + {children} + + + ) +} + +export const ScrollableInner = Inner + +export function Handle() { + return null +} + +/** + * TODO(eric) unused rn + */ +// export function Close() { +// const {_} = useLingui() +// const t = useTheme() +// const {close} = useDialogContext() +// return ( +// +// +// +// ) +// } diff --git a/src/components/Dialog/types.ts b/src/components/Dialog/types.ts new file mode 100644 index 0000000000..d36784183c --- /dev/null +++ b/src/components/Dialog/types.ts @@ -0,0 +1,43 @@ +import React from 'react' +import type {ViewStyle, AccessibilityProps} from 'react-native' +import {BottomSheetProps} from '@gorhom/bottom-sheet' + +type A11yProps = Required + +export type DialogContextProps = { + close: () => void +} + +export type DialogControlProps = { + open: (index?: number) => void + close: () => void +} + +export type DialogOuterProps = { + control: { + ref: React.RefObject + open: (index?: number) => void + close: () => void + } + onClose?: () => void + nativeOptions?: { + sheet?: Omit + } + webOptions?: {} +} + +type DialogInnerPropsBase = React.PropsWithChildren<{ + style?: ViewStyle +}> & + T +export type DialogInnerProps = + | DialogInnerPropsBase<{ + label?: undefined + accessibilityLabelledBy: A11yProps['aria-labelledby'] + accessibilityDescribedBy: string + }> + | DialogInnerPropsBase<{ + label: string + accessibilityLabelledBy?: undefined + accessibilityDescribedBy?: undefined + }> diff --git a/src/components/Link.tsx b/src/components/Link.tsx new file mode 100644 index 0000000000..8f686f3c4c --- /dev/null +++ b/src/components/Link.tsx @@ -0,0 +1,191 @@ +import React from 'react' +import { + Text, + TextStyle, + StyleProp, + GestureResponderEvent, + Linking, +} from 'react-native' +import { + useLinkProps, + useNavigation, + StackActions, +} from '@react-navigation/native' +import {sanitizeUrl} from '@braintree/sanitize-url' + +import {isWeb} from '#/platform/detection' +import {useTheme, web, flatten} from '#/alf' +import {Button, ButtonProps, useButtonContext} from '#/components/Button' +import {AllNavigatorParams, NavigationProp} from '#/lib/routes/types' +import { + convertBskyAppUrlIfNeeded, + isExternalUrl, + linkRequiresWarning, +} from '#/lib/strings/url-helpers' +import {useModalControls} from '#/state/modals' +import {router} from '#/routes' + +export type LinkProps = Omit< + ButtonProps, + 'style' | 'onPress' | 'disabled' | 'label' +> & { + /** + * `TextStyle` to apply to the anchor element itself. Does not apply to any children. + */ + style?: StyleProp + /** + * The React Navigation `StackAction` to perform when the link is pressed. + */ + action?: 'push' | 'replace' | 'navigate' + /** + * If true, will warn the user if the link text does not match the href. Only + * works for Links with children that are strings i.e. text links. + */ + warnOnMismatchingTextChild?: boolean + label?: ButtonProps['label'] +} & Pick>[0], 'to'> + +/** + * A interactive element that renders as a `` tag on the web. On mobile it + * will translate the `href` to navigator screens and params and dispatch a + * navigation action. + * + * Intended to behave as a web anchor tag. For more complex routing, use a + * `Button`. + */ +export function Link({ + children, + to, + action = 'push', + warnOnMismatchingTextChild, + style, + ...rest +}: LinkProps) { + const navigation = useNavigation() + const {href} = useLinkProps({ + to: + typeof to === 'string' ? convertBskyAppUrlIfNeeded(sanitizeUrl(to)) : to, + }) + const isExternal = isExternalUrl(href) + const {openModal, closeModal} = useModalControls() + const onPress = React.useCallback( + (e: GestureResponderEvent) => { + const stringChildren = typeof children === 'string' ? children : '' + const requiresWarning = Boolean( + warnOnMismatchingTextChild && + stringChildren && + isExternal && + linkRequiresWarning(href, stringChildren), + ) + + if (requiresWarning) { + e.preventDefault() + + openModal({ + name: 'link-warning', + text: stringChildren, + href: href, + }) + } else { + e.preventDefault() + + if (isExternal) { + Linking.openURL(href) + } else { + /** + * A `GestureResponderEvent`, but cast to `any` to avoid using a bunch + * of @ts-ignore below. + */ + const event = e as any + const isMiddleClick = isWeb && event.button === 1 + const isMetaKey = + isWeb && + (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) + const shouldOpenInNewTab = isMetaKey || isMiddleClick + + if ( + shouldOpenInNewTab || + href.startsWith('http') || + href.startsWith('mailto') + ) { + Linking.openURL(href) + } else { + closeModal() // close any active modals + + if (action === 'push') { + navigation.dispatch(StackActions.push(...router.matchPath(href))) + } else if (action === 'replace') { + navigation.dispatch( + StackActions.replace(...router.matchPath(href)), + ) + } else if (action === 'navigate') { + // @ts-ignore + navigation.navigate(...router.matchPath(href)) + } else { + throw Error('Unsupported navigator action.') + } + } + } + } + }, + [ + href, + isExternal, + warnOnMismatchingTextChild, + navigation, + action, + children, + closeModal, + openModal, + ], + ) + + return ( + + ) +} + +function LinkText({ + children, + style, +}: React.PropsWithChildren<{ + style?: StyleProp +}>) { + const t = useTheme() + const {hovered} = useButtonContext() + return ( + + {children as string} + + ) +} diff --git a/src/components/Portal.tsx b/src/components/Portal.tsx new file mode 100644 index 0000000000..1813d9e05e --- /dev/null +++ b/src/components/Portal.tsx @@ -0,0 +1,56 @@ +import React from 'react' + +type Component = React.ReactElement + +type ContextType = { + outlet: Component | null + append(id: string, component: Component): void + remove(id: string): void +} + +type ComponentMap = { + [id: string]: Component +} + +export const Context = React.createContext({ + outlet: null, + append: () => {}, + remove: () => {}, +}) + +export function Provider(props: React.PropsWithChildren<{}>) { + const map = React.useRef({}) + const [outlet, setOutlet] = React.useState(null) + + const append = React.useCallback((id, component) => { + if (map.current[id]) return + map.current[id] = {component} + setOutlet(<>{Object.values(map.current)}) + }, []) + + const remove = React.useCallback(id => { + delete map.current[id] + setOutlet(<>{Object.values(map.current)}) + }, []) + + return ( + + {props.children} + + ) +} + +export function Outlet() { + const ctx = React.useContext(Context) + return ctx.outlet +} + +export function Portal({children}: React.PropsWithChildren<{}>) { + const {append, remove} = React.useContext(Context) + const id = React.useId() + React.useEffect(() => { + append(id, children as Component) + return () => remove(id) + }, [id, children, append, remove]) + return null +} diff --git a/src/components/Prompt.tsx b/src/components/Prompt.tsx new file mode 100644 index 0000000000..7115f61901 --- /dev/null +++ b/src/components/Prompt.tsx @@ -0,0 +1,119 @@ +import React from 'react' +import {View, PressableProps} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useTheme, atoms as a} from '#/alf' +import {H4, P} from '#/components/Typography' +import {Button} from '#/components/Button' + +import * as Dialog from '#/components/Dialog' + +export {useDialogControl as usePromptControl} from '#/components/Dialog' + +const Context = React.createContext<{ + titleId: string + descriptionId: string +}>({ + titleId: '', + descriptionId: '', +}) + +export function Outer({ + children, + control, +}: React.PropsWithChildren<{ + control: Dialog.DialogOuterProps['control'] +}>) { + const titleId = React.useId() + const descriptionId = React.useId() + + const context = React.useMemo( + () => ({titleId, descriptionId}), + [titleId, descriptionId], + ) + + return ( + + + + + + {children} + + + + ) +} + +export function Title({children}: React.PropsWithChildren<{}>) { + const t = useTheme() + const {titleId} = React.useContext(Context) + return ( +

+ {children} +

+ ) +} + +export function Description({children}: React.PropsWithChildren<{}>) { + const t = useTheme() + const {descriptionId} = React.useContext(Context) + return ( +

+ {children} +

+ ) +} + +export function Actions({children}: React.PropsWithChildren<{}>) { + return ( + + {children} + + ) +} + +export function Cancel({ + children, +}: React.PropsWithChildren<{onPress?: PressableProps['onPress']}>) { + const {_} = useLingui() + const {close} = Dialog.useDialogContext() + return ( + + ) +} + +export function Action({ + children, + onPress, +}: React.PropsWithChildren<{onPress?: () => void}>) { + const {_} = useLingui() + const {close} = Dialog.useDialogContext() + const handleOnPress = React.useCallback(() => { + close() + onPress?.() + }, [close, onPress]) + return ( + + ) +} diff --git a/src/view/com/Typography.tsx b/src/components/Typography.tsx similarity index 63% rename from src/view/com/Typography.tsx rename to src/components/Typography.tsx index 6579c2e511..66cf0720de 100644 --- a/src/view/com/Typography.tsx +++ b/src/components/Typography.tsx @@ -1,6 +1,7 @@ import React from 'react' import {Text as RNText, TextProps} from 'react-native' -import {useTheme, atoms, web} from '#/alf' + +import {useTheme, atoms, web, flatten} from '#/alf' export function Text({style, ...rest}: TextProps) { const t = useTheme() @@ -18,7 +19,7 @@ export function H1({style, ...rest}: TextProps) { ) } @@ -34,7 +35,7 @@ export function H2({style, ...rest}: TextProps) { ) } @@ -50,7 +51,7 @@ export function H3({style, ...rest}: TextProps) { ) } @@ -66,7 +67,7 @@ export function H4({style, ...rest}: TextProps) { ) } @@ -82,7 +83,7 @@ export function H5({style, ...rest}: TextProps) { ) } @@ -98,7 +99,26 @@ export function H6({style, ...rest}: TextProps) { + ) +} + +export function P({style, ...rest}: TextProps) { + const t = useTheme() + const attr = + web({ + role: 'paragraph', + }) || {} + const _style = flatten(style) + const lineHeight = + (_style?.lineHeight || atoms.text_md.lineHeight) * + atoms.leading_normal.lineHeight + return ( + ) } diff --git a/src/components/forms/DateField/index.android.tsx b/src/components/forms/DateField/index.android.tsx new file mode 100644 index 0000000000..83fa285f5f --- /dev/null +++ b/src/components/forms/DateField/index.android.tsx @@ -0,0 +1,108 @@ +import React from 'react' +import {View, Pressable} from 'react-native' +import DateTimePicker, { + BaseProps as DateTimePickerProps, +} from '@react-native-community/datetimepicker' + +import {useTheme, atoms} from '#/alf' +import {Text} from '#/components/Typography' +import {useInteractionState} from '#/components/hooks/useInteractionState' +import * as TextField from '#/components/forms/TextField' +import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' + +import {DateFieldProps} from '#/components/forms/DateField/types' +import { + localizeDate, + toSimpleDateString, +} from '#/components/forms/DateField/utils' + +export * as utils from '#/components/forms/DateField/utils' +export const Label = TextField.Label + +export function DateField({ + value, + onChangeDate, + label, + isInvalid, + testID, +}: DateFieldProps) { + const t = useTheme() + const [open, setOpen] = React.useState(false) + const { + state: pressed, + onIn: onPressIn, + onOut: onPressOut, + } = useInteractionState() + const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() + + const {chromeFocus, chromeError, chromeErrorHover} = + TextField.useSharedInputStyles() + + const onChangeInternal = React.useCallback< + Required['onChange'] + >( + (_event, date) => { + setOpen(false) + + if (date) { + const formatted = toSimpleDateString(date) + onChangeDate(formatted) + } + }, + [onChangeDate, setOpen], + ) + + return ( + + setOpen(true)} + onPressIn={onPressIn} + onPressOut={onPressOut} + onFocus={onFocus} + onBlur={onBlur} + style={[ + { + paddingTop: 16, + paddingBottom: 16, + borderColor: 'transparent', + borderWidth: 2, + }, + atoms.flex_row, + atoms.flex_1, + atoms.w_full, + atoms.px_lg, + atoms.rounded_sm, + t.atoms.bg_contrast_50, + focused || pressed ? chromeFocus : {}, + isInvalid ? chromeError : {}, + isInvalid && (focused || pressed) ? chromeErrorHover : {}, + ]}> + + + + {localizeDate(value)} + + + + {open && ( + + )} + + ) +} diff --git a/src/components/forms/DateField/index.tsx b/src/components/forms/DateField/index.tsx new file mode 100644 index 0000000000..c359a9d460 --- /dev/null +++ b/src/components/forms/DateField/index.tsx @@ -0,0 +1,56 @@ +import React from 'react' +import {View} from 'react-native' +import DateTimePicker, { + DateTimePickerEvent, +} from '@react-native-community/datetimepicker' + +import {useTheme, atoms} from '#/alf' +import * as TextField from '#/components/forms/TextField' +import {toSimpleDateString} from '#/components/forms/DateField/utils' +import {DateFieldProps} from '#/components/forms/DateField/types' + +export * as utils from '#/components/forms/DateField/utils' +export const Label = TextField.Label + +/** + * Date-only input. Accepts a date in the format YYYY-MM-DD, and reports date + * changes in the same format. + * + * For dates of unknown format, convert with the + * `utils.toSimpleDateString(Date)` export of this file. + */ +export function DateField({ + value, + onChangeDate, + testID, + label, +}: DateFieldProps) { + const t = useTheme() + + const onChangeInternal = React.useCallback( + (event: DateTimePickerEvent, date: Date | undefined) => { + if (date) { + const formatted = toSimpleDateString(date) + onChangeDate(formatted) + } + }, + [onChangeDate], + ) + + return ( + + + + ) +} diff --git a/src/components/forms/DateField/index.web.tsx b/src/components/forms/DateField/index.web.tsx new file mode 100644 index 0000000000..32f38a5d16 --- /dev/null +++ b/src/components/forms/DateField/index.web.tsx @@ -0,0 +1,64 @@ +import React from 'react' +import {TextInput, TextInputProps, StyleSheet} from 'react-native' +// @ts-ignore +import {unstable_createElement} from 'react-native-web' + +import * as TextField from '#/components/forms/TextField' +import {toSimpleDateString} from '#/components/forms/DateField/utils' +import {DateFieldProps} from '#/components/forms/DateField/types' + +export * as utils from '#/components/forms/DateField/utils' +export const Label = TextField.Label + +const InputBase = React.forwardRef( + ({style, ...props}, ref) => { + return unstable_createElement('input', { + ...props, + ref, + type: 'date', + style: [ + StyleSheet.flatten(style), + { + background: 'transparent', + border: 0, + }, + ], + }) + }, +) + +InputBase.displayName = 'InputBase' + +const Input = TextField.createInput(InputBase as unknown as typeof TextInput) + +export function DateField({ + value, + onChangeDate, + label, + isInvalid, + testID, +}: DateFieldProps) { + const handleOnChange = React.useCallback( + (e: any) => { + const date = e.target.valueAsDate || e.target.value + + if (date) { + const formatted = toSimpleDateString(date) + onChangeDate(formatted) + } + }, + [onChangeDate], + ) + + return ( + + {}} + testID={testID} + /> + + ) +} diff --git a/src/components/forms/DateField/types.ts b/src/components/forms/DateField/types.ts new file mode 100644 index 0000000000..129f5672d4 --- /dev/null +++ b/src/components/forms/DateField/types.ts @@ -0,0 +1,7 @@ +export type DateFieldProps = { + value: string + onChangeDate: (date: string) => void + label: string + isInvalid?: boolean + testID?: string +} diff --git a/src/components/forms/DateField/utils.ts b/src/components/forms/DateField/utils.ts new file mode 100644 index 0000000000..c787272fe8 --- /dev/null +++ b/src/components/forms/DateField/utils.ts @@ -0,0 +1,16 @@ +import {getLocales} from 'expo-localization' + +const LOCALE = getLocales()[0] + +// we need the date in the form yyyy-MM-dd to pass to the input +export function toSimpleDateString(date: Date | string): string { + const _date = typeof date === 'string' ? new Date(date) : date + return _date.toISOString().split('T')[0] +} + +export function localizeDate(date: Date | string): string { + const _date = typeof date === 'string' ? new Date(date) : date + return new Intl.DateTimeFormat(LOCALE.languageTag, { + timeZone: 'UTC', + }).format(_date) +} diff --git a/src/components/forms/InputGroup.tsx b/src/components/forms/InputGroup.tsx new file mode 100644 index 0000000000..6908d4df8e --- /dev/null +++ b/src/components/forms/InputGroup.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms, useTheme} from '#/alf' + +/** + * NOT FINISHED, just here as a reference + */ +export function InputGroup(props: React.PropsWithChildren<{}>) { + const t = useTheme() + const children = React.Children.toArray(props.children) + const total = children.length + return ( + + {children.map((child, i) => { + return React.isValidElement(child) ? ( + + {i > 0 ? ( + + ) : null} + {React.cloneElement(child, { + // @ts-ignore + style: [ + ...(Array.isArray(child.props?.style) + ? child.props.style + : [child.props.style || {}]), + { + borderTopLeftRadius: i > 0 ? 0 : undefined, + borderTopRightRadius: i > 0 ? 0 : undefined, + borderBottomLeftRadius: i < total - 1 ? 0 : undefined, + borderBottomRightRadius: i < total - 1 ? 0 : undefined, + borderBottomWidth: i < total - 1 ? 0 : undefined, + }, + ], + })} + + ) : null + })} + + ) +} diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx new file mode 100644 index 0000000000..1ee58303ae --- /dev/null +++ b/src/components/forms/TextField.tsx @@ -0,0 +1,334 @@ +import React from 'react' +import { + View, + TextInput, + TextInputProps, + TextStyle, + ViewStyle, + Pressable, + StyleSheet, + AccessibilityProps, +} from 'react-native' + +import {HITSLOP_20} from 'lib/constants' +import {isWeb} from '#/platform/detection' +import {useTheme, atoms as a, web, tokens, android} from '#/alf' +import {Text} from '#/components/Typography' +import {useInteractionState} from '#/components/hooks/useInteractionState' +import {Props as SVGIconProps} from '#/components/icons/common' + +const Context = React.createContext<{ + inputRef: React.RefObject | null + isInvalid: boolean + hovered: boolean + onHoverIn: () => void + onHoverOut: () => void + focused: boolean + onFocus: () => void + onBlur: () => void +}>({ + inputRef: null, + isInvalid: false, + hovered: false, + onHoverIn: () => {}, + onHoverOut: () => {}, + focused: false, + onFocus: () => {}, + onBlur: () => {}, +}) + +export type RootProps = React.PropsWithChildren<{isInvalid?: boolean}> + +export function Root({children, isInvalid = false}: RootProps) { + const inputRef = React.useRef(null) + const rootRef = React.useRef(null) + const { + state: hovered, + onIn: onHoverIn, + onOut: onHoverOut, + } = useInteractionState() + const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() + + const context = React.useMemo( + () => ({ + inputRef, + hovered, + onHoverIn, + onHoverOut, + focused, + onFocus, + onBlur, + isInvalid, + }), + [ + inputRef, + hovered, + onHoverIn, + onHoverOut, + focused, + onFocus, + onBlur, + isInvalid, + ], + ) + + React.useLayoutEffect(() => { + const root = rootRef.current + if (!root || !isWeb) return + // @ts-ignore web only + root.tabIndex = -1 + }, []) + + return ( + + inputRef.current?.focus()} + onHoverIn={onHoverIn} + onHoverOut={onHoverOut}> + {children} + + + ) +} + +export function useSharedInputStyles() { + const t = useTheme() + return React.useMemo(() => { + const hover: ViewStyle[] = [ + { + borderColor: t.palette.contrast_100, + }, + ] + const focus: ViewStyle[] = [ + { + backgroundColor: t.palette.contrast_50, + borderColor: t.palette.primary_500, + }, + ] + const error: ViewStyle[] = [ + { + backgroundColor: + t.name === 'light' ? t.palette.negative_25 : t.palette.negative_900, + borderColor: + t.name === 'light' ? t.palette.negative_300 : t.palette.negative_800, + }, + ] + const errorHover: ViewStyle[] = [ + { + backgroundColor: + t.name === 'light' ? t.palette.negative_25 : t.palette.negative_900, + borderColor: tokens.color.red_500, + }, + ] + + return { + chromeHover: StyleSheet.flatten(hover), + chromeFocus: StyleSheet.flatten(focus), + chromeError: StyleSheet.flatten(error), + chromeErrorHover: StyleSheet.flatten(errorHover), + } + }, [t]) +} + +export type InputProps = Omit & { + label: string + value: string + onChangeText: (value: string) => void + isInvalid?: boolean +} + +export function createInput(Component: typeof TextInput) { + return function Input({ + label, + placeholder, + value, + onChangeText, + isInvalid, + ...rest + }: InputProps) { + const t = useTheme() + const ctx = React.useContext(Context) + const withinRoot = Boolean(ctx.inputRef) + + const {chromeHover, chromeFocus, chromeError, chromeErrorHover} = + useSharedInputStyles() + + if (!withinRoot) { + return ( + + + + ) + } + + return ( + <> + + + + + ) + } +} + +export const Input = createInput(TextInput) + +export function Label({children}: React.PropsWithChildren<{}>) { + const t = useTheme() + return ( + + {children} + + ) +} + +export function Icon({icon: Comp}: {icon: React.ComponentType}) { + const t = useTheme() + const ctx = React.useContext(Context) + const {hover, focus, errorHover, errorFocus} = React.useMemo(() => { + const hover: TextStyle[] = [ + { + color: t.palette.contrast_800, + }, + ] + const focus: TextStyle[] = [ + { + color: t.palette.primary_500, + }, + ] + const errorHover: TextStyle[] = [ + { + color: t.palette.negative_500, + }, + ] + const errorFocus: TextStyle[] = [ + { + color: t.palette.negative_500, + }, + ] + + return { + hover, + focus, + errorHover, + errorFocus, + } + }, [t]) + + return ( + + + + ) +} + +export function Suffix({ + children, + label, + accessibilityHint, +}: React.PropsWithChildren<{ + label: string + accessibilityHint?: AccessibilityProps['accessibilityHint'] +}>) { + const t = useTheme() + const ctx = React.useContext(Context) + return ( + + {children} + + ) +} diff --git a/src/components/forms/Toggle.tsx b/src/components/forms/Toggle.tsx new file mode 100644 index 0000000000..ad82bdff5f --- /dev/null +++ b/src/components/forms/Toggle.tsx @@ -0,0 +1,473 @@ +import React from 'react' +import {Pressable, View, ViewStyle} from 'react-native' + +import {HITSLOP_10} from 'lib/constants' +import {useTheme, atoms as a, web, native} from '#/alf' +import {Text} from '#/components/Typography' +import {useInteractionState} from '#/components/hooks/useInteractionState' + +export type ItemState = { + name: string + selected: boolean + disabled: boolean + isInvalid: boolean + hovered: boolean + pressed: boolean + focused: boolean +} + +const ItemContext = React.createContext({ + name: '', + selected: false, + disabled: false, + isInvalid: false, + hovered: false, + pressed: false, + focused: false, +}) + +const GroupContext = React.createContext<{ + values: string[] + disabled: boolean + type: 'radio' | 'checkbox' + maxSelectionsReached: boolean + setFieldValue: (props: {name: string; value: boolean}) => void +}>({ + type: 'checkbox', + values: [], + disabled: false, + maxSelectionsReached: false, + setFieldValue: () => {}, +}) + +export type GroupProps = React.PropsWithChildren<{ + type?: 'radio' | 'checkbox' + values: string[] + maxSelections?: number + disabled?: boolean + onChange: (value: string[]) => void + label: string +}> + +export type ItemProps = { + type?: 'radio' | 'checkbox' + name: string + label: string + value?: boolean + disabled?: boolean + onChange?: (selected: boolean) => void + isInvalid?: boolean + style?: (state: ItemState) => ViewStyle + children: ((props: ItemState) => React.ReactNode) | React.ReactNode +} + +export function useItemContext() { + return React.useContext(ItemContext) +} + +export function Group({ + children, + values: providedValues, + onChange, + disabled = false, + type = 'checkbox', + maxSelections, + label, +}: GroupProps) { + const groupRole = type === 'radio' ? 'radiogroup' : undefined + const values = type === 'radio' ? providedValues.slice(0, 1) : providedValues + const [maxReached, setMaxReached] = React.useState(false) + + const setFieldValue = React.useCallback< + (props: {name: string; value: boolean}) => void + >( + ({name, value}) => { + if (type === 'checkbox') { + const pruned = values.filter(v => v !== name) + const next = value ? pruned.concat(name) : pruned + onChange(next) + } else { + onChange([name]) + } + }, + [type, onChange, values], + ) + + React.useEffect(() => { + if (type === 'checkbox') { + if ( + maxSelections && + values.length >= maxSelections && + maxReached === false + ) { + setMaxReached(true) + } else if ( + maxSelections && + values.length < maxSelections && + maxReached === true + ) { + setMaxReached(false) + } + } + }, [type, values.length, maxSelections, maxReached, setMaxReached]) + + const context = React.useMemo( + () => ({ + values, + type, + disabled, + maxSelectionsReached: maxReached, + setFieldValue, + }), + [values, disabled, type, maxReached, setFieldValue], + ) + + return ( + + + {children} + + + ) +} + +export function Item({ + children, + name, + value = false, + disabled: itemDisabled = false, + onChange, + isInvalid, + style, + type = 'checkbox', + label, + ...rest +}: ItemProps) { + const { + values: selectedValues, + type: groupType, + disabled: groupDisabled, + setFieldValue, + maxSelectionsReached, + } = React.useContext(GroupContext) + const { + state: hovered, + onIn: onHoverIn, + onOut: onHoverOut, + } = useInteractionState() + const { + state: pressed, + onIn: onPressIn, + onOut: onPressOut, + } = useInteractionState() + const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() + + const role = groupType === 'radio' ? 'radio' : type + const selected = selectedValues.includes(name) || !!value + const disabled = + groupDisabled || itemDisabled || (!selected && maxSelectionsReached) + + const onPress = React.useCallback(() => { + const next = !selected + setFieldValue({name, value: next}) + onChange?.(next) + }, [name, selected, onChange, setFieldValue]) + + const state = React.useMemo( + () => ({ + name, + selected, + disabled: disabled ?? false, + isInvalid: isInvalid ?? false, + hovered, + pressed, + focused, + }), + [name, selected, disabled, hovered, pressed, focused, isInvalid], + ) + + return ( + + + {typeof children === 'function' ? children(state) : children} + + + ) +} + +export function Label({children}: React.PropsWithChildren<{}>) { + const t = useTheme() + const {disabled} = useItemContext() + return ( + + {children} + + ) +} + +// TODO(eric) refactor to memoize styles without knowledge of state +export function createSharedToggleStyles({ + theme: t, + hovered, + focused, + selected, + disabled, + isInvalid, +}: { + theme: ReturnType + selected: boolean + hovered: boolean + focused: boolean + disabled: boolean + isInvalid: boolean +}) { + const base: ViewStyle[] = [] + const baseHover: ViewStyle[] = [] + const indicator: ViewStyle[] = [] + + if (selected) { + base.push({ + backgroundColor: + t.name === 'light' ? t.palette.primary_25 : t.palette.primary_900, + borderColor: t.palette.primary_500, + }) + + if (hovered || focused) { + baseHover.push({ + backgroundColor: + t.name === 'light' ? t.palette.primary_100 : t.palette.primary_800, + borderColor: + t.name === 'light' ? t.palette.primary_600 : t.palette.primary_400, + }) + } + } else { + if (hovered || focused) { + baseHover.push({ + backgroundColor: + t.name === 'light' ? t.palette.contrast_50 : t.palette.contrast_100, + borderColor: t.palette.contrast_500, + }) + } + } + + if (isInvalid) { + base.push({ + backgroundColor: + t.name === 'light' ? t.palette.negative_25 : t.palette.negative_900, + borderColor: + t.name === 'light' ? t.palette.negative_300 : t.palette.negative_800, + }) + + if (hovered || focused) { + baseHover.push({ + backgroundColor: + t.name === 'light' ? t.palette.negative_25 : t.palette.negative_900, + borderColor: t.palette.negative_500, + }) + } + } + + if (disabled) { + base.push({ + backgroundColor: t.palette.contrast_100, + borderColor: t.palette.contrast_400, + }) + } + + return { + baseStyles: base, + baseHoverStyles: disabled ? [] : baseHover, + indicatorStyles: indicator, + } +} + +export function Checkbox() { + const t = useTheme() + const {selected, hovered, focused, disabled, isInvalid} = useItemContext() + const {baseStyles, baseHoverStyles, indicatorStyles} = + createSharedToggleStyles({ + theme: t, + hovered, + focused, + selected, + disabled, + isInvalid, + }) + return ( + + {selected ? ( + + ) : null} + + ) +} + +export function Switch() { + const t = useTheme() + const {selected, hovered, focused, disabled, isInvalid} = useItemContext() + const {baseStyles, baseHoverStyles, indicatorStyles} = + createSharedToggleStyles({ + theme: t, + hovered, + focused, + selected, + disabled, + isInvalid, + }) + return ( + + + + ) +} + +export function Radio() { + const t = useTheme() + const {selected, hovered, focused, disabled, isInvalid} = + React.useContext(ItemContext) + const {baseStyles, baseHoverStyles, indicatorStyles} = + createSharedToggleStyles({ + theme: t, + hovered, + focused, + selected, + disabled, + isInvalid, + }) + return ( + + {selected ? ( + + ) : null} + + ) +} diff --git a/src/components/forms/ToggleButton.tsx b/src/components/forms/ToggleButton.tsx new file mode 100644 index 0000000000..615fedae8b --- /dev/null +++ b/src/components/forms/ToggleButton.tsx @@ -0,0 +1,124 @@ +import React from 'react' +import {View, AccessibilityProps, TextStyle, ViewStyle} from 'react-native' + +import {atoms as a, useTheme, native} from '#/alf' +import {Text} from '#/components/Typography' + +import * as Toggle from '#/components/forms/Toggle' + +export type ItemProps = Omit & + AccessibilityProps & + React.PropsWithChildren<{}> + +export type GroupProps = Omit & { + multiple?: boolean +} + +export function Group({children, multiple, ...props}: GroupProps) { + const t = useTheme() + return ( + + + {children} + + + ) +} + +export function Button({children, ...props}: ItemProps) { + return ( + + {children} + + ) +} + +function ButtonInner({children}: React.PropsWithChildren<{}>) { + const t = useTheme() + const state = Toggle.useItemContext() + + const {baseStyles, hoverStyles, activeStyles, textStyles} = + React.useMemo(() => { + const base: ViewStyle[] = [] + const hover: ViewStyle[] = [] + const active: ViewStyle[] = [] + const text: TextStyle[] = [] + + hover.push( + t.name === 'light' ? t.atoms.bg_contrast_100 : t.atoms.bg_contrast_25, + ) + + if (state.selected) { + active.push({ + backgroundColor: t.palette.contrast_800, + }) + text.push(t.atoms.text_inverted) + hover.push({ + backgroundColor: t.palette.contrast_800, + }) + + if (state.disabled) { + active.push({ + backgroundColor: t.palette.contrast_500, + }) + } + } + + if (state.disabled) { + base.push({ + backgroundColor: t.palette.contrast_100, + }) + text.push({ + opacity: 0.5, + }) + } + + return { + baseStyles: base, + hoverStyles: hover, + activeStyles: active, + textStyles: text, + } + }, [t, state]) + + return ( + + {typeof children === 'string' ? ( + + {children} + + ) : ( + children + )} + + ) +} diff --git a/src/components/hooks/useInteractionState.ts b/src/components/hooks/useInteractionState.ts new file mode 100644 index 0000000000..653b1c10e6 --- /dev/null +++ b/src/components/hooks/useInteractionState.ts @@ -0,0 +1,21 @@ +import React from 'react' + +export function useInteractionState() { + const [state, setState] = React.useState(false) + + const onIn = React.useCallback(() => { + setState(true) + }, [setState]) + const onOut = React.useCallback(() => { + setState(false) + }, [setState]) + + return React.useMemo( + () => ({ + state, + onIn, + onOut, + }), + [state, onIn, onOut], + ) +} diff --git a/src/components/icons/ArrowTopRight.tsx b/src/components/icons/ArrowTopRight.tsx new file mode 100644 index 0000000000..92ad30a129 --- /dev/null +++ b/src/components/icons/ArrowTopRight.tsx @@ -0,0 +1,5 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const ArrowTopRight_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M8 6a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1v9a1 1 0 1 1-2 0V8.414l-9.793 9.793a1 1 0 0 1-1.414-1.414L15.586 7H9a1 1 0 0 1-1-1Z', +}) diff --git a/src/components/icons/CalendarDays.tsx b/src/components/icons/CalendarDays.tsx new file mode 100644 index 0000000000..72cc48e261 --- /dev/null +++ b/src/components/icons/CalendarDays.tsx @@ -0,0 +1,5 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const CalendarDays_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M4 3a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H4Zm1 16V9h14v10H5ZM5 7h14V5H5v2Zm3 10.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM17.25 12a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM12 13.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM9.25 12a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM12 17.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Z', +}) diff --git a/src/components/icons/ColorPalette.tsx b/src/components/icons/ColorPalette.tsx new file mode 100644 index 0000000000..157fa7fa13 --- /dev/null +++ b/src/components/icons/ColorPalette.tsx @@ -0,0 +1,5 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const ColorPalette_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M4 12c0-4.09 3.527-7.5 8-7.5s8 3.41 8 7.5c0 1.579-.419 2.056-.708 2.236-.388.241-1.031.286-2.058.153-.33-.043-.652-.096-.991-.152a65.905 65.905 0 0 0-.531-.087c-.52-.081-1.077-.156-1.61-.164-1.065-.016-2.336.245-2.996 1.567-.418.834-.295 1.67-.078 2.314.18.534.47 1.055.683 1.437v.001l.097.175.01.018C7.432 19.407 4 16.033 4 12Zm8-9.5C6.532 2.5 2 6.7 2 12s4.532 9.5 10 9.5c.401 0 .812-.04 1.166-.193.41-.176.761-.517.866-1.028.085-.416-.03-.796-.118-1.029a5.981 5.981 0 0 0-.351-.73l-.12-.215c-.215-.392-.403-.73-.52-1.078-.13-.387-.111-.614-.029-.78.146-.291.404-.473 1.178-.461.385.005.825.06 1.329.14.15.023.308.05.47.077.36.059.742.122 1.105.17 1.021.132 2.325.213 3.373-.439C21.496 15.22 22 13.874 22 12c0-5.3-4.532-9.5-10-9.5Zm3.5 8.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM9 12.25a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm1.5-2.75a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z', +}) diff --git a/src/components/icons/Globe.tsx b/src/components/icons/Globe.tsx new file mode 100644 index 0000000000..f81b3ff7a0 --- /dev/null +++ b/src/components/icons/Globe.tsx @@ -0,0 +1,5 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const Globe_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M4.062 11h2.961c.103-2.204.545-4.218 1.235-5.77.06-.136.123-.269.188-.399A8.007 8.007 0 0 0 4.062 11ZM12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm0 2c-.227 0-.518.1-.868.432-.354.337-.719.872-1.047 1.61-.561 1.263-.958 2.991-1.06 4.958h5.95c-.102-1.967-.499-3.695-1.06-4.958-.328-.738-.693-1.273-1.047-1.61C12.518 4.099 12.227 4 12 4Zm4.977 7c-.103-2.204-.545-4.218-1.235-5.77a9.78 9.78 0 0 0-.188-.399A8.006 8.006 0 0 1 19.938 11h-2.961Zm-2.003 2H9.026c.101 1.966.498 3.695 1.06 4.958.327.738.692 1.273 1.046 1.61.35.333.641.432.868.432.227 0 .518-.1.868-.432.354-.337.719-.872 1.047-1.61.561-1.263.958-2.991 1.06-4.958Zm.58 6.169c.065-.13.128-.263.188-.399.69-1.552 1.132-3.566 1.235-5.77h2.961a8.006 8.006 0 0 1-4.384 6.169Zm-7.108 0a9.877 9.877 0 0 1-.188-.399c-.69-1.552-1.132-3.566-1.235-5.77H4.062a8.006 8.006 0 0 0 4.384 6.169Z', +}) diff --git a/src/components/icons/TEMPLATE.tsx b/src/components/icons/TEMPLATE.tsx new file mode 100644 index 0000000000..9fc1470375 --- /dev/null +++ b/src/components/icons/TEMPLATE.tsx @@ -0,0 +1,48 @@ +import React from 'react' +import Svg, {Path} from 'react-native-svg' + +import {useCommonSVGProps, Props} from '#/components/icons/common' + +export const IconTemplate_Stroke2_Corner0_Rounded = React.forwardRef( + function LogoImpl(props: Props, ref) { + const {fill, size, style, ...rest} = useCommonSVGProps(props) + + return ( + + + + ) + }, +) + +export function createSinglePathSVG({path}: {path: string}) { + return React.forwardRef(function LogoImpl(props, ref) { + const {fill, size, style, ...rest} = useCommonSVGProps(props) + + return ( + + + + ) + }) +} diff --git a/src/components/icons/common.ts b/src/components/icons/common.ts new file mode 100644 index 0000000000..9e9f15c4dd --- /dev/null +++ b/src/components/icons/common.ts @@ -0,0 +1,32 @@ +import {StyleSheet, TextProps} from 'react-native' +import type {SvgProps, PathProps} from 'react-native-svg' + +import {tokens} from '#/alf' + +export type Props = { + fill?: PathProps['fill'] + style?: TextProps['style'] + size?: keyof typeof sizes +} & Omit + +export const sizes = { + xs: 12, + sm: 16, + md: 20, + lg: 24, + xl: 28, +} + +export function useCommonSVGProps(props: Props) { + const {fill, size, ...rest} = props + const style = StyleSheet.flatten(rest.style) + const _fill = fill || style?.color || tokens.color.blue_500 + const _size = Number(size ? sizes[size] : rest.width || sizes.md) + + return { + fill: _fill, + size: _size, + style, + ...rest, + } +} diff --git a/src/lib/api/feed/custom.ts b/src/lib/api/feed/custom.ts index 94cbff1305..41c5367e57 100644 --- a/src/lib/api/feed/custom.ts +++ b/src/lib/api/feed/custom.ts @@ -4,15 +4,20 @@ import { } from '@atproto/api' import {FeedAPI, FeedAPIResponse} from './types' import {getAgent} from '#/state/session' +import {getContentLanguages} from '#/state/preferences/languages' export class CustomFeedAPI implements FeedAPI { constructor(public params: GetCustomFeed.QueryParams) {} async peekLatest(): Promise { - const res = await getAgent().app.bsky.feed.getFeed({ - ...this.params, - limit: 1, - }) + const contentLangs = getContentLanguages().join(',') + const res = await getAgent().app.bsky.feed.getFeed( + { + ...this.params, + limit: 1, + }, + {headers: {'Accept-Language': contentLangs}}, + ) return res.data.feed[0] } @@ -23,11 +28,15 @@ export class CustomFeedAPI implements FeedAPI { cursor: string | undefined limit: number }): Promise { - const res = await getAgent().app.bsky.feed.getFeed({ - ...this.params, - cursor, - limit, - }) + const contentLangs = getContentLanguages().join(',') + const res = await getAgent().app.bsky.feed.getFeed( + { + ...this.params, + cursor, + limit, + }, + {headers: {'Accept-Language': contentLangs}}, + ) if (res.success) { // NOTE // some custom feeds fail to enforce the pagination limit diff --git a/src/lib/api/feed/home.ts b/src/lib/api/feed/home.ts new file mode 100644 index 0000000000..436a66d076 --- /dev/null +++ b/src/lib/api/feed/home.ts @@ -0,0 +1,89 @@ +import {AppBskyFeedDefs} from '@atproto/api' +import {FeedAPI, FeedAPIResponse} from './types' +import {FollowingFeedAPI} from './following' +import {CustomFeedAPI} from './custom' +import {PROD_DEFAULT_FEED} from '#/lib/constants' + +// HACK +// the feed API does not include any facilities for passing down +// non-post elements. adding that is a bit of a heavy lift, and we +// have just one temporary usecase for it: flagging when the home feed +// falls back to discover. +// we use this fallback marker post to drive this instead. see Feed.tsx +// for the usage. +// -prf +export const FALLBACK_MARKER_POST: AppBskyFeedDefs.FeedViewPost = { + post: { + uri: 'fallback-marker-post', + cid: 'fake', + record: {}, + author: { + did: 'did:fake', + handle: 'fake.com', + }, + indexedAt: new Date().toISOString(), + }, +} + +export class HomeFeedAPI implements FeedAPI { + following: FollowingFeedAPI + discover: CustomFeedAPI + usingDiscover = false + itemCursor = 0 + + constructor() { + this.following = new FollowingFeedAPI() + this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')}) + } + + reset() { + this.following = new FollowingFeedAPI() + this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')}) + this.usingDiscover = false + this.itemCursor = 0 + } + + async peekLatest(): Promise { + if (this.usingDiscover) { + return this.discover.peekLatest() + } + return this.following.peekLatest() + } + + async fetch({ + cursor, + limit, + }: { + cursor: string | undefined + limit: number + }): Promise { + if (!cursor) { + this.reset() + } + + let returnCursor + let posts: AppBskyFeedDefs.FeedViewPost[] = [] + + if (!this.usingDiscover) { + const res = await this.following.fetch({cursor, limit}) + returnCursor = res.cursor + posts = posts.concat(res.feed) + if (!returnCursor) { + cursor = '' + posts.push(FALLBACK_MARKER_POST) + this.usingDiscover = true + } + } + + if (this.usingDiscover) { + const res = await this.discover.fetch({cursor, limit}) + returnCursor = res.cursor + posts = posts.concat(res.feed) + } + + return { + cursor: returnCursor, + feed: posts, + } + } +} diff --git a/src/lib/api/feed/merge.ts b/src/lib/api/feed/merge.ts index 2314e2b954..28bf143cbb 100644 --- a/src/lib/api/feed/merge.ts +++ b/src/lib/api/feed/merge.ts @@ -8,6 +8,7 @@ import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types' import {FeedParams} from '#/state/queries/post-feed' import {FeedTunerFn} from '../feed-manip' import {getAgent} from '#/state/session' +import {getContentLanguages} from '#/state/preferences/languages' const REQUEST_WAIT_MS = 500 // 500ms const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours @@ -25,7 +26,7 @@ export class MergeFeedAPI implements FeedAPI { reset() { this.following = new MergeFeedSource_Following(this.feedTuners) - this.customFeeds = [] // just empty the array, they will be captured in _fetchNext() + this.customFeeds = [] this.feedCursor = 0 this.itemCursor = 0 this.sampleCursor = 0 @@ -231,11 +232,15 @@ class MergeFeedSource_Custom extends MergeFeedSource { limit: number, ): Promise { try { - const res = await getAgent().app.bsky.feed.getFeed({ - cursor, - limit, - feed: this.feedUri, - }) + const contentLangs = getContentLanguages().join(',') + const res = await getAgent().app.bsky.feed.getFeed( + { + cursor, + limit, + feed: this.feedUri, + }, + {headers: {'Accept-Language': contentLangs}}, + ) // NOTE // some custom feeds fail to enforce the pagination limit // so we manually truncate here diff --git a/src/lib/strings/embed-player.ts b/src/lib/strings/embed-player.ts index 0f97eb0800..3270b6f07a 100644 --- a/src/lib/strings/embed-player.ts +++ b/src/lib/strings/embed-player.ts @@ -68,11 +68,12 @@ export function parseEmbedPlayerFromUrl( // youtube if (urlp.hostname === 'youtu.be') { const videoId = urlp.pathname.split('/')[1] + const seek = encodeURIComponent(urlp.searchParams.get('t') ?? 0) if (videoId) { return { type: 'youtube_video', source: 'youtube', - playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1&playsinline=1`, + playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1&playsinline=1&start=${seek}`, } } } @@ -84,13 +85,14 @@ export function parseEmbedPlayerFromUrl( const [_, page, shortVideoId] = urlp.pathname.split('/') const videoId = page === 'shorts' ? shortVideoId : (urlp.searchParams.get('v') as string) + const seek = encodeURIComponent(urlp.searchParams.get('t') ?? 0) if (videoId) { return { type: page === 'shorts' ? 'youtube_short' : 'youtube_video', source: page === 'shorts' ? 'youtubeShorts' : 'youtube', hideDetails: page === 'shorts' ? true : undefined, - playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1&playsinline=1`, + playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1&playsinline=1&start=${seek}`, } } } diff --git a/src/lib/themes.ts b/src/lib/themes.ts index ad7574db6e..2d4515c778 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -2,30 +2,32 @@ import {Platform} from 'react-native' import type {Theme} from './ThemeContext' import {colors} from './styles' +import {darkPalette, lightPalette} from '#/alf/themes' + export const defaultTheme: Theme = { colorScheme: 'light', palette: { default: { - background: colors.white, - backgroundLight: colors.gray1, - text: colors.black, - textLight: colors.gray5, - textInverted: colors.white, - link: colors.blue3, - border: '#f0e9e9', - borderDark: '#e0d9d9', - icon: colors.gray4, + background: lightPalette.white, + backgroundLight: lightPalette.contrast_50, + text: lightPalette.black, + textLight: lightPalette.contrast_700, + textInverted: lightPalette.white, + link: lightPalette.primary_500, + border: lightPalette.contrast_100, + borderDark: lightPalette.contrast_200, + icon: lightPalette.contrast_500, // non-standard - textVeryLight: colors.gray4, - replyLine: colors.gray2, - replyLineDot: colors.gray3, - unreadNotifBg: '#ebf6ff', - unreadNotifBorder: colors.blue1, - postCtrl: '#71768A', - brandText: '#0066FF', - emptyStateIcon: '#B6B6C9', - borderLinkHover: '#cac1c1', + textVeryLight: lightPalette.contrast_400, + replyLine: lightPalette.contrast_100, + replyLineDot: lightPalette.contrast_200, + unreadNotifBg: lightPalette.primary_25, + unreadNotifBorder: lightPalette.primary_100, + postCtrl: lightPalette.contrast_500, + brandText: lightPalette.primary_500, + emptyStateIcon: lightPalette.contrast_300, + borderLinkHover: lightPalette.contrast_300, }, primary: { background: colors.blue3, @@ -50,15 +52,15 @@ export const defaultTheme: Theme = { icon: colors.green4, }, inverted: { - background: colors.black, - backgroundLight: colors.gray6, - text: colors.white, - textLight: colors.gray3, - textInverted: colors.black, - link: colors.blue2, - border: colors.gray3, - borderDark: colors.gray2, - icon: colors.gray5, + background: darkPalette.black, + backgroundLight: darkPalette.contrast_50, + text: darkPalette.white, + textLight: darkPalette.contrast_700, + textInverted: darkPalette.black, + link: darkPalette.primary_500, + border: darkPalette.contrast_100, + borderDark: darkPalette.contrast_200, + icon: darkPalette.contrast_500, }, error: { background: colors.red3, @@ -292,26 +294,26 @@ export const darkTheme: Theme = { palette: { ...defaultTheme.palette, default: { - background: colors.black, - backgroundLight: colors.gray7, - text: colors.white, - textLight: colors.gray3, - textInverted: colors.black, - link: colors.blue3, - border: colors.gray7, - borderDark: colors.gray6, - icon: colors.gray4, + background: darkPalette.black, + backgroundLight: darkPalette.contrast_50, + text: darkPalette.white, + textLight: darkPalette.contrast_700, + textInverted: darkPalette.black, + link: darkPalette.primary_500, + border: darkPalette.contrast_100, + borderDark: darkPalette.contrast_200, + icon: darkPalette.contrast_500, // non-standard - textVeryLight: colors.gray4, - replyLine: colors.gray5, - replyLineDot: colors.gray6, - unreadNotifBg: colors.blue7, - unreadNotifBorder: colors.blue6, - postCtrl: '#707489', - brandText: '#0085ff', - emptyStateIcon: colors.gray4, - borderLinkHover: colors.gray5, + textVeryLight: darkPalette.contrast_400, + replyLine: darkPalette.contrast_100, + replyLineDot: darkPalette.contrast_200, + unreadNotifBg: darkPalette.primary_975, + unreadNotifBorder: darkPalette.primary_900, + postCtrl: darkPalette.contrast_500, + brandText: darkPalette.primary_500, + emptyStateIcon: darkPalette.contrast_300, + borderLinkHover: darkPalette.contrast_300, }, primary: { ...defaultTheme.palette.primary, @@ -322,15 +324,15 @@ export const darkTheme: Theme = { textInverted: colors.green2, }, inverted: { - background: colors.white, - backgroundLight: colors.gray2, - text: colors.black, - textLight: colors.gray5, - textInverted: colors.white, - link: colors.blue3, - border: colors.gray3, - borderDark: colors.gray4, - icon: colors.gray1, + background: lightPalette.white, + backgroundLight: lightPalette.contrast_50, + text: lightPalette.black, + textLight: lightPalette.contrast_700, + textInverted: lightPalette.white, + link: lightPalette.primary_500, + border: lightPalette.contrast_100, + borderDark: lightPalette.contrast_200, + icon: lightPalette.contrast_500, }, }, } diff --git a/src/locale/helpers.ts b/src/locale/helpers.ts index 8b3bf5f3d8..dddd6855cc 100644 --- a/src/locale/helpers.ts +++ b/src/locale/helpers.ts @@ -137,6 +137,8 @@ export function sanitizeAppLanguageSetting(appLanguage: string): AppLanguage { return AppLanguage.pt_BR case 'uk': return AppLanguage.uk + case 'ca': + return AppLanguage.ca default: continue } diff --git a/src/locale/i18n.ts b/src/locale/i18n.ts index 88ae10b624..d0bc828cfa 100644 --- a/src/locale/i18n.ts +++ b/src/locale/i18n.ts @@ -13,6 +13,7 @@ import {messages as messagesJa} from '#/locale/locales/ja/messages' import {messages as messagesKo} from '#/locale/locales/ko/messages' import {messages as messagesPt_BR} from '#/locale/locales/pt-BR/messages' import {messages as messagesUk} from '#/locale/locales/uk/messages' +import {messages as messagesCa} from '#/locale/locales/ca/messages' import {sanitizeAppLanguageSetting} from '#/locale/helpers' import {AppLanguage} from '#/locale/languages' @@ -59,6 +60,10 @@ export async function dynamicActivate(locale: AppLanguage) { i18n.loadAndActivate({locale, messages: messagesUk}) break } + case AppLanguage.ca: { + i18n.loadAndActivate({locale, messages: messagesCa}) + break + } default: { i18n.loadAndActivate({locale, messages: messagesEn}) break diff --git a/src/locale/i18n.web.ts b/src/locale/i18n.web.ts index a6f0e158d1..de5e5aa78e 100644 --- a/src/locale/i18n.web.ts +++ b/src/locale/i18n.web.ts @@ -49,6 +49,10 @@ export async function dynamicActivate(locale: AppLanguage) { mod = await import(`./locales/uk/messages`) break } + case AppLanguage.ca: { + mod = await import(`./locales/ca/messages`) + break + } default: { mod = await import(`./locales/en/messages`) break diff --git a/src/locale/languages.ts b/src/locale/languages.ts index c6799816cf..7b19fbe2fc 100644 --- a/src/locale/languages.ts +++ b/src/locale/languages.ts @@ -16,6 +16,7 @@ export enum AppLanguage { ko = 'ko', pt_BR = 'pt-BR', uk = 'uk', + ca = 'ca', } interface AppLanguageConfig { @@ -35,6 +36,7 @@ export const APP_LANGUAGES: AppLanguageConfig[] = [ {code2: AppLanguage.ko, name: '한국어'}, {code2: AppLanguage.pt_BR, name: 'Português (BR)'}, {code2: AppLanguage.uk, name: 'Українська'}, + {code2: AppLanguage.ca, name: 'Catalan'}, ] export const LANGUAGES: Language[] = [ diff --git a/src/locale/locales/ca/messages.po b/src/locale/locales/ca/messages.po new file mode 100644 index 0000000000..3dd32d85b6 --- /dev/null +++ b/src/locale/locales/ca/messages.po @@ -0,0 +1,2489 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2024-01-05 11:44+0530\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: ca\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: Ivan Beà\n" +"Language-Team: \n" +"X-Poedit-Language: Catalan\n" +"X-Poedit-Country: SPAIN\n" +"X-Poedit-SourceCharset: utf-8\n" + +#: src/view/shell/desktop/RightNav.tsx:168 +msgid "{0, plural, one {# invite code available} other {# invite codes available}}" +msgstr "{0, plural, one {# codi d'invitació disponible} other {# codis d'invitació disponibles}}" + +#: src/view/com/modals/Repost.tsx:44 +msgid "{0}" +msgstr "{0}" + +#: src/view/com/modals/CreateOrEditList.tsx:176 +msgid "{0} {purposeLabel} List" +msgstr "Llista {purposeLabel} {0}" + +#: src/view/shell/desktop/RightNav.tsx:151 +msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}" +msgstr "{invitesAvailable, plural, one {Codis d'invitació: # available} other {Codi d'invitació: # available}}" + +#: src/view/screens/Settings.tsx:407 +#: src/view/shell/Drawer.tsx:659 +msgid "{invitesAvailable} invite code available" +msgstr "{invitesAvailable} codi d'invitació disponible" + +#: src/view/screens/Settings.tsx:409 +#: src/view/shell/Drawer.tsx:661 +msgid "{invitesAvailable} invite codes available" +msgstr "{invitesAvailable} codis d'invitació disponibles" + +#: src/view/screens/Search/Search.tsx:87 +msgid "{message}" +msgstr "{message}" + +#: src/view/com/threadgate/WhoCanReply.tsx:158 +msgid "<0/> members" +msgstr "<0/> membres" + +#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:30 +msgid "<0>Choose your<1>Recommended<2>Feeds" +msgstr "<0>Tria els teus<1>canals<2>recomanats" + +#: src/view/com/auth/onboarding/RecommendedFollows.tsx:37 +msgid "<0>Follow some<1>Recommended<2>Users" +msgstr "<0>Segueix alguns<1>usuaris<2>recomanats" + +#: src/view/com/util/moderation/LabelInfo.tsx:45 +msgid "A content warning has been applied to this {0}." +msgstr "S'ha aplicat una advertència de contingut a {0}" + +#: src/lib/hooks/useOTAUpdate.ts:16 +msgid "A new version of the app is available. Please update to continue using the app." +msgstr "Hi ha una nova versió d'aquesta aplicació. Actualitza-la per continuar." + +#: src/view/com/modals/EditImage.tsx:299 +#: src/view/screens/Settings.tsx:417 +msgid "Accessibility" +msgstr "Accessibilitat" + +#: src/view/com/auth/login/LoginForm.tsx:159 +#: src/view/screens/Settings.tsx:286 +msgid "Account" +msgstr "Compte" + +#: src/view/com/util/AccountDropdownBtn.tsx:41 +msgid "Account options" +msgstr "Opcions del compte" + +#: src/view/com/modals/ListAddRemoveUsers.tsx:264 +#: src/view/com/modals/UserAddRemoveLists.tsx:193 +#: src/view/screens/ProfileList.tsx:763 +msgid "Add" +msgstr "Afegeix" + +#: src/view/com/modals/SelfLabel.tsx:56 +msgid "Add a content warning" +msgstr "Afegeix una advertència de contingut" + +#: src/view/screens/ProfileList.tsx:753 +msgid "Add a user to this list" +msgstr "Afegeix un usuari a aquesta llista" + +#: src/view/screens/Settings.tsx:355 +#: src/view/screens/Settings.tsx:364 +msgid "Add account" +msgstr "Afegeix un compte" + +#: src/view/com/composer/photos/Gallery.tsx:119 +#: src/view/com/composer/photos/Gallery.tsx:180 +msgid "Add alt text" +msgstr "Afegeix text alternatiu" + +#: src/view/com/modals/report/InputIssueDetails.tsx:41 +#: src/view/com/modals/report/Modal.tsx:191 +msgid "Add details" +msgstr "Afegeix detalls" + +#: src/view/com/modals/report/Modal.tsx:194 +msgid "Add details to report" +msgstr "Afegeix detalls a l'informe" + +#: src/view/com/composer/Composer.tsx:442 +msgid "Add link card" +msgstr "Afegeix una targeta a l'enllaç" + +#: src/view/com/composer/Composer.tsx:445 +msgid "Add link card:" +msgstr "Afegeix una targeta a l'enllaç:" + +#: src/view/com/modals/ChangeHandle.tsx:415 +msgid "Add the following DNS record to your domain:" +msgstr "Afegeix el següent registre DNS al teu domini:" + +#: src/view/com/profile/ProfileHeader.tsx:353 +msgid "Add to Lists" +msgstr "Afegeix a les llistes" + +#: src/view/screens/ProfileFeed.tsx:271 +msgid "Add to my feeds" +msgstr "Afegeix als meus canals" + +#: src/view/com/modals/ListAddRemoveUsers.tsx:191 +#: src/view/com/modals/UserAddRemoveLists.tsx:128 +msgid "Added to list" +msgstr "Afegit a la llista" + +#: src/view/screens/PreferencesHomeFeed.tsx:164 +msgid "Adjust the number of likes a reply must have to be shown in your feed." +msgstr "Ajusta el nombre de m'agrades que hagi de tenir una resposta per aparèixer al teu canal." + +#: src/view/com/modals/SelfLabel.tsx:75 +msgid "Adult Content" +msgstr "Contingut per a adults" + +#: src/view/screens/Settings.tsx:569 +msgid "Advanced" +msgstr "Avançat" + +#: src/view/com/composer/photos/Gallery.tsx:130 +msgid "ALT" +msgstr "ALT" + +#: src/view/com/modals/EditImage.tsx:315 +msgid "Alt text" +msgstr "Text alternatiu" + +#: src/view/com/composer/photos/Gallery.tsx:209 +msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone." +msgstr "El text alternatiu descriu les imatges per a les persones cegues o amb problemes de visió, i ajuda a donar context a tothom." + +#: src/view/com/modals/VerifyEmail.tsx:118 +msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below." +msgstr "S'ha enviat un correu a {0}. Inclou un codi de confirmació que has d'entrar aquí sota." + +#: 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 "S'ha enviat un correu a la teva adreça prèvia, {0}. Inclou un codi de confirmació que has d'entrar aquí sota." + +#: src/view/com/notifications/FeedItem.tsx:237 +#: src/view/com/threadgate/WhoCanReply.tsx:178 +msgid "and" +msgstr "i" + +#: src/view/screens/LanguageSettings.tsx:95 +msgid "App Language" +msgstr "Idioma de l'aplicació" + +#: src/view/screens/Settings.tsx:589 +msgid "App passwords" +msgstr "Contrasenyes de l'aplicació" + +#: src/view/screens/AppPasswords.tsx:186 +msgid "App Passwords" +msgstr "Contrasenyes de l'aplicació" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:236 +msgid "Appeal content warning" +msgstr "Advertència d'apel·lació sobre el contingut" + +#: src/view/com/modals/AppealLabel.tsx:65 +msgid "Appeal Content Warning" +msgstr "Advertència d'apel·lació sobre el contingut" + +#: src/view/com/util/moderation/LabelInfo.tsx:52 +msgid "Appeal this decision" +msgstr "Apel·la aquesta decisió" + +#: src/view/com/util/moderation/LabelInfo.tsx:56 +msgid "Appeal this decision." +msgstr "Apel·la aquesta decisió." + +#: src/view/screens/Settings.tsx:432 +msgid "Appearance" +msgstr "Aparença" + +#: src/view/screens/AppPasswords.tsx:223 +msgid "Are you sure you want to delete the app password \"{name}\"?" +msgstr "Confirmes que vols eliminar la contrasenya de l'aplicació \"{name}\"?" + +#: src/view/com/composer/Composer.tsx:142 +msgid "Are you sure you'd like to discard this draft?" +msgstr "Confirmes que vols descartar aquest esborrany?" + +#: src/view/screens/ProfileList.tsx:353 +msgid "Are you sure?" +msgstr "Ho confirmes?" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:219 +msgid "Are you sure? This cannot be undone." +msgstr "Ho confirmes? Aquesta acció no es pot desfer." + +#: src/view/com/modals/SelfLabel.tsx:123 +msgid "Artistic or non-erotic nudity." +msgstr "Nuesa artística o no eròtica." + +#: src/view/com/auth/create/CreateAccount.tsx:141 +#: src/view/com/auth/login/ChooseAccountForm.tsx:151 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:166 +#: src/view/com/auth/login/LoginForm.tsx:250 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:148 +#: src/view/com/modals/report/InputIssueDetails.tsx:46 +#: src/view/com/post-thread/PostThread.tsx:388 +#: src/view/com/post-thread/PostThread.tsx:438 +#: src/view/com/post-thread/PostThread.tsx:446 +#: src/view/com/profile/ProfileHeader.tsx:672 +msgid "Back" +msgstr "Endarrere" + +#: src/view/screens/Settings.tsx:461 +msgid "Basics" +msgstr "Conceptes bàsics" + +#: src/view/com/auth/create/Step2.tsx:156 +#: src/view/com/modals/BirthDateSettings.tsx:73 +msgid "Birthday" +msgstr "Aniversari" + +#: src/view/screens/Settings.tsx:312 +msgid "Birthday:" +msgstr "Aniversari:" + +#: src/view/com/profile/ProfileHeader.tsx:282 +#: src/view/com/profile/ProfileHeader.tsx:389 +msgid "Block Account" +msgstr "Bloqueja el compte" + +#: src/view/screens/ProfileList.tsx:523 +msgid "Block accounts" +msgstr "Bloqueja comptes" + +#: src/view/screens/ProfileList.tsx:473 +msgid "Block list" +msgstr "Bloqueja una llista" + +#: src/view/screens/ProfileList.tsx:308 +msgid "Block these accounts?" +msgstr "Vols bloquejar aquests comptes?" + +#: src/view/screens/Moderation.tsx:123 +msgid "Blocked accounts" +msgstr "Comptes bloquejats" + +#: src/view/screens/ModerationBlockedAccounts.tsx:107 +msgid "Blocked Accounts" +msgstr "Comptes bloquejats" + +#: src/view/com/profile/ProfileHeader.tsx:284 +msgid "Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you." +msgstr "Els comptes bloquejats no poden respondre cap fil teu, ni anomenar-te ni interactuar amb tu de cap manera." + +#: src/view/screens/ModerationBlockedAccounts.tsx:115 +msgid "Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you. You will not see their content and they will be prevented from seeing yours." +msgstr "Els comptes bloquejats no poden respondre a cap fil teu, ni anomenar-te ni interactuar amb tu de cap manera. No veuràs mai el seu contingut ni ells el teu." + +#: src/view/com/post-thread/PostThread.tsx:250 +msgid "Blocked post." +msgstr "Publicació bloquejada." + +#: src/view/screens/ProfileList.tsx:310 +msgid "Blocking is public. Blocked accounts cannot reply in your threads, mention you, or otherwise interact with you." +msgstr "El bloqueig és públic. Els comptes bloquejats no poden respondre els teus fils, ni mencionar-te ni interactuar amb tu de cap manera." + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:93 +msgid "Blog" +msgstr "Blog" + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:31 +msgid "Bluesky" +msgstr "Bluesky" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:80 +msgid "Bluesky is flexible." +msgstr "Bluesky és flexible." + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:69 +msgid "Bluesky is open." +msgstr "Bluesky és obert." + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:56 +msgid "Bluesky is public." +msgstr "Bluesky és públic." + +#: 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 "Bluesky utilitza les invitacions per construir una comunitat saludable. Si no coneixes ningú amb invitacions, pots apuntar-te a la llista d'espera i te n'enviarem una aviat." + +#: src/view/screens/Moderation.tsx:225 +msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private." +msgstr "Bluesky no mostrarà el teu perfil ni les publicacions als usuaris que no estiguin registrats. Altres aplicacions poden no seguir aquesta demanda. Això no fa que el teu compte sigui privat." + +#: src/view/com/modals/ServerInput.tsx:78 +msgid "Bluesky.Social" +msgstr "Bluesky.Social" + +#: src/view/screens/Settings.tsx:718 +msgid "Build version {0} {1}" +msgstr "Versió {0} {1}" + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:87 +msgid "Business" +msgstr "Negocis" + +#: src/view/com/composer/photos/OpenCameraBtn.tsx:60 +#: src/view/com/util/UserAvatar.tsx:221 +#: src/view/com/util/UserBanner.tsx:38 +msgid "Camera" +msgstr "Càmera" + +#: src/view/com/modals/AddAppPasswords.tsx:214 +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 "Només pot tenir lletres, números, espais, guions i guions baixos. Ha de tenir almenys 4 caràcters i no més de 32." + +#: src/view/com/composer/Composer.tsx:289 +#: src/view/com/composer/Composer.tsx:292 +#: src/view/com/modals/AltImage.tsx:128 +#: src/view/com/modals/ChangeEmail.tsx:218 +#: src/view/com/modals/ChangeEmail.tsx:220 +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/CreateOrEditList.tsx:267 +#: src/view/com/modals/CreateOrEditList.tsx:272 +#: src/view/com/modals/DeleteAccount.tsx:150 +#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/EditImage.tsx:323 +#: src/view/com/modals/EditProfile.tsx:248 +#: src/view/com/modals/LinkWarning.tsx:85 +#: src/view/com/modals/Repost.tsx:73 +#: src/view/com/modals/Waitlist.tsx:136 +#: src/view/screens/Search/Search.tsx:601 +#: src/view/shell/desktop/Search.tsx:182 +msgid "Cancel" +msgstr "Cancel·la" + +#: src/view/com/modals/DeleteAccount.tsx:146 +#: src/view/com/modals/DeleteAccount.tsx:219 +msgid "Cancel account deletion" +msgstr "Cancel·la la supressió del compte" + +#: src/view/com/modals/AltImage.tsx:123 +msgid "Cancel add image alt text" +msgstr "Cancel·la afegir text a la imatge" + +#: src/view/com/modals/ChangeHandle.tsx:149 +msgid "Cancel change handle" +msgstr "Cancel·la el canvi d'identificador" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:134 +msgid "Cancel image crop" +msgstr "Cancel·la la retallada de la imatge" + +#: src/view/com/modals/EditProfile.tsx:243 +msgid "Cancel profile editing" +msgstr "Cancel·la l'edició del perfil" + +#: src/view/com/modals/Repost.tsx:64 +msgid "Cancel quote post" +msgstr "Cancel·la la citació de la publicació" + +#: src/view/com/modals/ListAddRemoveUsers.tsx:87 +#: src/view/shell/desktop/Search.tsx:178 +msgid "Cancel search" +msgstr "Cancel·la la cerca" + +#: src/view/com/modals/Waitlist.tsx:132 +msgid "Cancel waitlist signup" +msgstr "Cancel·la la inscripció a la llista d'espera" + +#: src/view/screens/Settings.tsx:306 +msgid "Change" +msgstr "Canvia" + +#: src/view/screens/Settings.tsx:601 +#: src/view/screens/Settings.tsx:610 +msgid "Change handle" +msgstr "Canvia l'identificador" + +#: src/view/com/modals/ChangeHandle.tsx:161 +msgid "Change Handle" +msgstr "Canvia l'identificador" + +#: src/view/com/modals/VerifyEmail.tsx:141 +msgid "Change my email" +msgstr "Canvia el meu correu" + +#: src/view/com/modals/ChangeEmail.tsx:109 +msgid "Change Your Email" +msgstr "Canvia el teu correu" + +#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:121 +msgid "Check out some recommended feeds. Tap + to add them to your list of pinned feeds." +msgstr "Mira alguns canals recomanats. Prem + per afegir-los als teus canals fixats." + +#: src/view/com/auth/onboarding/RecommendedFollows.tsx:185 +msgid "Check out some recommended users. Follow them to see similar users." +msgstr "Mira alguns usuaris recomanats. Segueix-los per veure altres usuaris similars." + +#: src/view/com/modals/DeleteAccount.tsx:163 +msgid "Check your inbox for an email with the confirmation code to enter below:" +msgstr "Comprova el teu correu per rebre el codi de confirmació i entra'l aquí sota:" + +#: src/view/com/modals/Threadgate.tsx:72 +msgid "Choose \"Everybody\" or \"Nobody\"" +msgstr "Tria \"Tothom\" or \"Ningú\"" + +#: src/view/com/modals/ServerInput.tsx:38 +msgid "Choose Service" +msgstr "Tria un servei" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:83 +msgid "Choose the algorithms that power your experience with custom feeds." +msgstr "Tria els algoritmes que potenciaran la teva experiència amb els canals personalitzats." + +#: src/view/com/auth/create/Step2.tsx:127 +msgid "Choose your password" +msgstr "Tria la teva contrasenya." + +#: src/view/screens/Settings.tsx:694 +msgid "Clear all legacy storage data" +msgstr "Esborra totes les dades antigues emmagatzemades" + +#: src/view/screens/Settings.tsx:696 +msgid "Clear all legacy storage data (restart after this)" +msgstr "Esborra totes les dades antigues emmagatzemades (i després reinicia)" + +#: src/view/screens/Settings.tsx:706 +msgid "Clear all storage data" +msgstr "Esborra totes les dades emmagatzemades" + +#: src/view/screens/Settings.tsx:708 +msgid "Clear all storage data (restart after this)" +msgstr "Esborra totes les dades emmagatzemades (i després reinicia)" + +#: src/view/com/util/forms/SearchInput.tsx:74 +#: src/view/screens/Search/Search.tsx:582 +msgid "Clear search query" +msgstr "Esborra la cerca" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:38 +msgid "Close alert" +msgstr "Tanca l'advertència" + +#: src/view/com/util/BottomSheetCustomBackdrop.tsx:33 +msgid "Close bottom drawer" +msgstr "Tanca el calaix inferior" + +#: src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx:26 +msgid "Close image" +msgstr "Tanca la imatge" + +#: src/view/com/lightbox/Lightbox.web.tsx:112 +msgid "Close image viewer" +msgstr "Tanca el visor d'imatges" + +#: src/view/shell/index.web.tsx:49 +msgid "Close navigation footer" +msgstr "Tanca el peu de la navegació" + +#: src/view/screens/CommunityGuidelines.tsx:32 +msgid "Community Guidelines" +msgstr "Directrius de la comunitat" + +#: src/view/com/composer/Prompt.tsx:24 +msgid "Compose reply" +msgstr "Redacta una resposta" + +#: src/view/com/modals/AppealLabel.tsx:98 +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/SelfLabel.tsx:154 +#: src/view/com/modals/VerifyEmail.tsx:225 +#: src/view/screens/PreferencesHomeFeed.tsx:299 +#: src/view/screens/PreferencesThreads.tsx:153 +msgid "Confirm" +msgstr "Confirma" + +#: src/view/com/modals/ChangeEmail.tsx:193 +#: src/view/com/modals/ChangeEmail.tsx:195 +msgid "Confirm Change" +msgstr "Confirma el canvi" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:34 +msgid "Confirm content language settings" +msgstr "Confirma la configuració de l'idioma del contingut" + +#: src/view/com/modals/DeleteAccount.tsx:209 +msgid "Confirm delete account" +msgstr "Confirma l'eliminació del compte" + +#: src/view/com/modals/ChangeEmail.tsx:157 +#: src/view/com/modals/DeleteAccount.tsx:176 +#: src/view/com/modals/VerifyEmail.tsx:159 +msgid "Confirmation code" +msgstr "Codi de confirmació" + +#: src/view/com/auth/create/CreateAccount.tsx:174 +#: src/view/com/auth/login/LoginForm.tsx:269 +msgid "Connecting..." +msgstr "Connectant…" + +#: src/view/screens/Moderation.tsx:81 +msgid "Content filtering" +msgstr "Filtre de contingut" + +#: src/view/com/modals/ContentFilteringSettings.tsx:44 +msgid "Content Filtering" +msgstr "Filtre de contingut" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:74 +#: src/view/screens/LanguageSettings.tsx:278 +msgid "Content Languages" +msgstr "Idiomes del contingut" + +#: src/view/com/util/moderation/ScreenHider.tsx:78 +msgid "Content Warning" +msgstr "Advertència del contingut" + +#: src/view/com/composer/labels/LabelsBtn.tsx:31 +msgid "Content warnings" +msgstr "Advertències del contingut" + +#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:148 +#: src/view/com/auth/onboarding/RecommendedFollows.tsx:209 +msgid "Continue" +msgstr "Continua" + +#: src/view/com/modals/AddAppPasswords.tsx:193 +#: src/view/com/modals/InviteCodes.tsx:179 +msgid "Copied" +msgstr "Copiat" + +#: src/view/com/modals/AddAppPasswords.tsx:186 +msgid "Copy" +msgstr "Copia" + +#: src/view/screens/ProfileList.tsx:385 +msgid "Copy link to list" +msgstr "Copia l'enllaç a la llista" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:139 +msgid "Copy link to post" +msgstr "Copia l'enllaç a la publicació" + +#: src/view/com/profile/ProfileHeader.tsx:338 +msgid "Copy link to profile" +msgstr "Copia l'enllaç al perfil" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:125 +msgid "Copy post text" +msgstr "Copia el text de la publicació" + +#: src/view/screens/CopyrightPolicy.tsx:29 +msgid "Copyright Policy" +msgstr "Política de drets d'autor" + +#: src/view/screens/ProfileFeed.tsx:95 +msgid "Could not load feed" +msgstr "No es pot carregar el canal" + +#: src/view/screens/ProfileList.tsx:839 +msgid "Could not load list" +msgstr "No es pot carregar la llista" + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:62 +#: src/view/com/auth/SplashScreen.tsx:46 +msgid "Create a new account" +msgstr "Crea un nou compte" + +#: src/view/com/auth/create/CreateAccount.tsx:121 +msgid "Create Account" +msgstr "Crea un compte" + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:54 +#: src/view/com/auth/SplashScreen.tsx:43 +msgid "Create new account" +msgstr "Crea un nou compte" + +#: src/view/screens/AppPasswords.tsx:248 +msgid "Created {0}" +msgstr "Creat {0}" + +#: src/view/com/modals/ChangeHandle.tsx:387 +#: src/view/com/modals/ServerInput.tsx:102 +msgid "Custom domain" +msgstr "Domini personalitzat" + +#: src/view/screens/Settings.tsx:615 +msgid "Danger Zone" +msgstr "Zona de perill" + +#: src/view/screens/Settings.tsx:622 +msgid "Delete account" +msgstr "Elimina el compte" + +#: src/view/com/modals/DeleteAccount.tsx:83 +msgid "Delete Account" +msgstr "Elimina el compte" + +#: src/view/screens/AppPasswords.tsx:221 +#: src/view/screens/AppPasswords.tsx:241 +msgid "Delete app password" +msgstr "Elimina la contrasenya d'aplicació" + +#: src/view/screens/ProfileList.tsx:352 +#: src/view/screens/ProfileList.tsx:412 +msgid "Delete List" +msgstr "Elimina la llista" + +#: src/view/com/modals/DeleteAccount.tsx:212 +msgid "Delete my account" +msgstr "Elimina el meu compte" + +#: src/view/screens/Settings.tsx:632 +msgid "Delete my account…" +msgstr "Elimina el meu compte…" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:214 +msgid "Delete post" +msgstr "Elimina la publicació" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:218 +msgid "Delete this post?" +msgstr "Vols eliminar aquesta publicació?" + +#: src/view/com/post-thread/PostThread.tsx:242 +msgid "Deleted post." +msgstr "Publicació eliminada." + +#: src/view/com/modals/CreateOrEditList.tsx:218 +#: src/view/com/modals/CreateOrEditList.tsx:234 +#: src/view/com/modals/EditProfile.tsx:197 +#: src/view/com/modals/EditProfile.tsx:209 +msgid "Description" +msgstr "Descripció" + +#: src/view/com/auth/create/Step1.tsx:96 +msgid "Dev Server" +msgstr "Servidor de desenvolupament" + +#: src/view/screens/Settings.tsx:637 +msgid "Developer Tools" +msgstr "Eines de desenvolupador" + +#: src/view/com/composer/Composer.tsx:210 +msgid "Did you want to say anything?" +msgstr "Vols dir alguna cosa?" + +#: src/view/com/composer/Composer.tsx:143 +msgid "Discard" +msgstr "Descarta" + +#: src/view/com/composer/Composer.tsx:137 +msgid "Discard draft" +msgstr "Descarta l'esborrany" + +#: src/view/screens/Moderation.tsx:207 +msgid "Discourage apps from showing my account to logged-out users" +msgstr "Evita que les aplicacions mostrin el meu compte als usuaris no connectats" + +#: src/view/screens/Feeds.tsx:405 +msgid "Discover new feeds" +msgstr "Descobreix nous canals" + +#: src/view/com/modals/EditProfile.tsx:191 +msgid "Display name" +msgstr "Nom mostrat" + +#: src/view/com/modals/EditProfile.tsx:179 +msgid "Display Name" +msgstr "Nom mostrat" + +#: src/view/com/modals/ChangeHandle.tsx:485 +msgid "Domain verified!" +msgstr "Domini verificat!" + +#: src/view/com/auth/onboarding/RecommendedFollows.tsx:86 +#: src/view/com/modals/ContentFilteringSettings.tsx:88 +#: src/view/com/modals/ContentFilteringSettings.tsx:96 +#: src/view/com/modals/crop-image/CropImage.web.tsx:152 +#: src/view/com/modals/EditImage.tsx:333 +#: src/view/com/modals/ListAddRemoveUsers.tsx:142 +#: src/view/com/modals/SelfLabel.tsx:157 +#: src/view/com/modals/Threadgate.tsx:129 +#: src/view/com/modals/Threadgate.tsx:132 +#: src/view/com/modals/UserAddRemoveLists.tsx:79 +#: src/view/screens/PreferencesHomeFeed.tsx:302 +#: src/view/screens/PreferencesThreads.tsx:156 +msgid "Done" +msgstr "Fet" + +#: src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx:42 +msgid "Done{extraText}" +msgstr "Fet{extraText}" + +#: src/view/com/modals/InviteCodes.tsx:94 +msgid "Each code works once. You'll receive more invite codes periodically." +msgstr "Cada codi funciona un cop. Rebràs més codis d'invitació periòdicament." + +#: src/view/com/composer/photos/Gallery.tsx:144 +#: src/view/com/modals/EditImage.tsx:207 +msgid "Edit image" +msgstr "Edita la imatge" + +#: src/view/screens/ProfileList.tsx:400 +msgid "Edit list details" +msgstr "Edita els detalls de la llista" + +#: src/view/screens/Feeds.tsx:367 +#: src/view/screens/SavedFeeds.tsx:84 +msgid "Edit My Feeds" +msgstr "Edita els meus canals" + +#: src/view/com/modals/EditProfile.tsx:151 +msgid "Edit my profile" +msgstr "Edita el meu perfil" + +#: src/view/com/profile/ProfileHeader.tsx:453 +msgid "Edit profile" +msgstr "Edita el perfil" + +#: src/view/com/profile/ProfileHeader.tsx:456 +msgid "Edit Profile" +msgstr "Edita el perfil" + +#: src/view/screens/Feeds.tsx:330 +msgid "Edit Saved Feeds" +msgstr "Edita els meus canals guardats" + +#: src/view/com/auth/create/Step2.tsx:108 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:148 +#: src/view/com/modals/ChangeEmail.tsx:141 +#: src/view/com/modals/Waitlist.tsx:88 +msgid "Email" +msgstr "Correu" + +#: src/view/com/auth/create/Step2.tsx:99 +msgid "Email address" +msgstr "Adreça de correu" + +#: src/view/com/modals/ChangeEmail.tsx:111 +msgid "Email Updated" +msgstr "Correu actualitzat" + +#: src/view/screens/Settings.tsx:290 +msgid "Email:" +msgstr "Correu:" + +#: src/view/screens/PreferencesHomeFeed.tsx:138 +msgid "Enable this setting to only see replies between people you follow." +msgstr "Activa aquesta opció per veure només les respostes entre els comptes que segueixes" + +#: src/view/screens/Profile.tsx:426 +msgid "End of feed" +msgstr "Fi del canal" + +#: src/view/com/auth/create/Step1.tsx:71 +msgid "Enter the address of your provider:" +msgstr "Introdueix l'adreça del teu proveïdor:" + +#: src/view/com/modals/ChangeHandle.tsx:369 +msgid "Enter the domain you want to use" +msgstr "Introdueix el domini que vols utilitzar" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:101 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "Introdueix el correu que vas fer servir per crear el teu compte. T'enviarem un \"codi de restabliment\" perquè puguis posar una nova contrasenya." + +#: src/view/com/auth/create/Step2.tsx:104 +msgid "Enter your email address" +msgstr "Introdueix el teu correu" + +#: src/view/com/modals/ChangeEmail.tsx:117 +msgid "Enter your new email address below." +msgstr "Introdueix el teu nou correu a continuació." + +#: src/view/com/auth/login/Login.tsx:99 +msgid "Enter your username and password" +msgstr "Introdueix el teu usuari i contrasenya" + +#: src/view/screens/Search/Search.tsx:105 +msgid "Error:" +msgstr "Error:" + +#: src/view/com/modals/Threadgate.tsx:76 +msgid "Everybody" +msgstr "Tothom" + +#: src/view/com/lightbox/Lightbox.web.tsx:156 +msgid "Expand alt text" +msgstr "Expandeix el text alternatiu" + +#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:109 +#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:141 +msgid "Failed to load recommended feeds" +msgstr "Error en carregar els canals recomanats" + +#: src/view/screens/Feeds.tsx:556 +msgid "Feed offline" +msgstr "Canal fora de línia" + +#: src/view/com/feeds/FeedPage.tsx:143 +msgid "Feed Preferences" +msgstr "Preferències del canal" + +#: src/view/shell/desktop/RightNav.tsx:73 +#: src/view/shell/Drawer.tsx:311 +msgid "Feedback" +msgstr "Comentaris" + +#: src/view/screens/Feeds.tsx:475 +#: src/view/screens/Profile.tsx:165 +#: src/view/shell/bottom-bar/BottomBar.tsx:181 +#: src/view/shell/desktop/LeftNav.tsx:342 +#: src/view/shell/Drawer.tsx:474 +#: src/view/shell/Drawer.tsx:475 +msgid "Feeds" +msgstr "Canals" + +#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:57 +msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting." +msgstr "Els canals són creats pels usuaris per curar contingut. Tria els canals que trobis interessants." + +#: src/view/screens/SavedFeeds.tsx:156 +msgid "Feeds are custom algorithms that users build with a little coding expertise. <0/> for more information." +msgstr "Els canals són algoritmes personalitzats creats per usuaris que coneixen una mica de codi. <0/> per a més informació." + +#: src/view/screens/Search/Search.tsx:427 +msgid "Find users on Bluesky" +msgstr "Troba usuaris a Bluesky" + +#: src/view/screens/Search/Search.tsx:425 +msgid "Find users with the search tool on the right" +msgstr "Troba usuaris amb l'eina de cerca de la dreta" + +#: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:150 +msgid "Finding similar accounts..." +msgstr "Troba comptes similars…" + +#: src/view/screens/PreferencesHomeFeed.tsx:102 +msgid "Fine-tune the content you see on your home screen." +msgstr "Ajusta el contingut que es veu a la teva pantalla d'inici." + +#: src/view/screens/PreferencesThreads.tsx:60 +msgid "Fine-tune the discussion threads." +msgstr "Ajusta els fils de discussió." + +#: src/view/com/profile/ProfileHeader.tsx:538 +msgid "Follow" +msgstr "Segueix" + +#: src/view/com/auth/onboarding/RecommendedFollows.tsx:64 +msgid "Follow some users to get started. We can recommend you more users based on who you find interesting." +msgstr "Segueix a alguns usuaris per començar. Te'n podem recomanar més basant-nos en els que trobes interessants." + +#: src/view/com/modals/Threadgate.tsx:98 +msgid "Followed users" +msgstr "Usuaris seguits" + +#: src/view/screens/PreferencesHomeFeed.tsx:145 +msgid "Followed users only" +msgstr "Només els usuaris seguits" + +#: src/view/screens/ProfileFollowers.tsx:25 +msgid "Followers" +msgstr "Seguidors" + +#: src/view/com/profile/ProfileHeader.tsx:624 +msgid "following" +msgstr "seguint" + +#: src/view/com/profile/ProfileHeader.tsx:522 +#: src/view/screens/ProfileFollows.tsx:25 +msgid "Following" +msgstr "Seguint" + +#: src/view/com/profile/ProfileHeader.tsx:571 +msgid "Follows you" +msgstr "Et segueix" + +#: src/view/com/modals/DeleteAccount.tsx:107 +msgid "For security reasons, we'll need to send a confirmation code to your email address." +msgstr "Per motius de seguretat necessitem enviar-te un codi de confirmació al teu correu." + +#: src/view/com/modals/AddAppPasswords.tsx:207 +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 "Per motius de seguretat no podràs tornar-la a veure. Si perds aquesta contrasenya necessitaràs generar-ne una de nova." + +#: src/view/com/auth/login/LoginForm.tsx:232 +msgid "Forgot" +msgstr "L'he oblidat" + +#: src/view/com/auth/login/LoginForm.tsx:229 +msgid "Forgot password" +msgstr "He oblidat la contrasenya" + +#: src/view/com/auth/login/Login.tsx:127 +#: src/view/com/auth/login/Login.tsx:143 +msgid "Forgot Password" +msgstr "He oblidat la contrasenya" + +#: src/view/com/composer/photos/SelectPhotoBtn.tsx:43 +msgid "Gallery" +msgstr "Galeria" + +#: src/view/com/modals/VerifyEmail.tsx:183 +msgid "Get Started" +msgstr "Comença" + +#: src/view/com/auth/LoggedOut.tsx:81 +#: src/view/com/auth/LoggedOut.tsx:82 +#: src/view/com/util/moderation/ScreenHider.tsx:123 +#: src/view/shell/desktop/LeftNav.tsx:104 +msgid "Go back" +msgstr "Ves enrere" + +#: src/view/screens/ProfileFeed.tsx:104 +#: src/view/screens/ProfileFeed.tsx:109 +#: src/view/screens/ProfileList.tsx:848 +#: src/view/screens/ProfileList.tsx:853 +msgid "Go Back" +msgstr "Ves enrere" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:181 +#: src/view/com/auth/login/LoginForm.tsx:279 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:163 +msgid "Go to next" +msgstr "Ves al següent" + +#: src/view/com/modals/ChangeHandle.tsx:265 +msgid "Handle" +msgstr "Identificador" + +#: src/view/shell/desktop/RightNav.tsx:102 +#: src/view/shell/Drawer.tsx:321 +msgid "Help" +msgstr "Ajuda" + +#: src/view/com/modals/AddAppPasswords.tsx:148 +msgid "Here is your app password." +msgstr "Aquí tens la teva contrasenya d'aplicació." + +#: src/view/com/notifications/FeedItem.tsx:324 +#: src/view/com/util/moderation/ContentHider.tsx:103 +msgid "Hide" +msgstr "Amaga" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:173 +msgid "Hide post" +msgstr "Amaga l'entrada" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:177 +msgid "Hide this post?" +msgstr "Vols amagar aquesta entrada?" + +#: src/view/com/notifications/FeedItem.tsx:316 +msgid "Hide user list" +msgstr "Amaga la llista d'usuaris" + +#: src/view/com/posts/FeedErrorMessage.tsx:110 +msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue." +msgstr "S'ha produït algun error quan s'intentava connectar amb el servidor del canal. Avisa al propietari del canal d'aquest problema." + +#: src/view/com/posts/FeedErrorMessage.tsx:98 +msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue." +msgstr "Sembla que el servidor del canal està mal configurat. Avisa al propietari del canal d'aquest problema." + +#: src/view/com/posts/FeedErrorMessage.tsx:104 +msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue." +msgstr "Sembla que el servidor del canal està sense connexió. Avisa al propietari del canal d'aquest problema." + +#: src/view/com/posts/FeedErrorMessage.tsx:101 +msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue." +msgstr "El servidor del canal ha donat una resposta incorrecta. Avisa al propietari del canal d'aquest problema." + +#: src/view/com/posts/FeedErrorMessage.tsx:95 +msgid "Hmm, we're having trouble finding this feed. It may have been deleted." +msgstr "Tenim problemes per trobar aquest canal. Potser ha estat eliminat." + +#: src/view/shell/bottom-bar/BottomBar.tsx:137 +#: src/view/shell/desktop/LeftNav.tsx:306 +#: src/view/shell/Drawer.tsx:398 +#: src/view/shell/Drawer.tsx:399 +msgid "Home" +msgstr "Inici" + +#: src/view/com/pager/FeedsTabBarMobile.tsx:96 +#: src/view/screens/PreferencesHomeFeed.tsx:95 +#: src/view/screens/Settings.tsx:481 +msgid "Home Feed Preferences" +msgstr "Preferències dels canals a l'inici" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:114 +msgid "Hosting provider" +msgstr "Proveïdor d'allotjament" + +#: src/view/com/auth/create/Step1.tsx:76 +#: src/view/com/auth/create/Step1.tsx:81 +msgid "Hosting provider address" +msgstr "Adreça del proveïdor d'allotjament" + +#: src/view/com/modals/VerifyEmail.tsx:208 +msgid "I have a code" +msgstr "Tinc un codi" + +#: src/view/com/modals/ChangeHandle.tsx:281 +msgid "I have my own domain" +msgstr "Tinc el meu propi domini" + +#: src/view/com/modals/SelfLabel.tsx:127 +msgid "If none are selected, suitable for all ages." +msgstr "Si no en selecciones cap, és apropiat per a totes les edats." + +#: src/view/com/modals/AltImage.tsx:97 +msgid "Image alt text" +msgstr "Text alternatiu de la imatge" + +#: src/view/com/util/UserAvatar.tsx:308 +#: src/view/com/util/UserBanner.tsx:116 +msgid "Image options" +msgstr "Opcions de la imatge" + +#: src/view/com/auth/login/LoginForm.tsx:113 +msgid "Invalid username or password" +msgstr "Nom d'usuari o contrasenya incorrectes" + +#: src/view/screens/Settings.tsx:383 +msgid "Invite" +msgstr "Convida" + +#: src/view/com/modals/InviteCodes.tsx:91 +#: src/view/screens/Settings.tsx:371 +msgid "Invite a Friend" +msgstr "Convida un amic" + +#: src/view/com/auth/create/Step2.tsx:72 +msgid "Invite code" +msgstr "Codi d'invitació" + +#: src/view/com/auth/create/state.ts:136 +msgid "Invite code not accepted. Check that you input it correctly and try again." +msgstr "Codi d'invitació rebutjat. Comprova que l'has entrat correctament i torna-ho a provar." + +#: src/view/shell/Drawer.tsx:640 +msgid "Invite codes: {invitesAvailable} available" +msgstr "Codis d'invitació: {invitesAvailable} disponibles" + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:99 +msgid "Jobs" +msgstr "Feines" + +#: src/view/com/modals/Waitlist.tsx:67 +msgid "Join the waitlist" +msgstr "Uneix-te a la llista d'espera" + +#: src/view/com/auth/create/Step2.tsx:86 +#: src/view/com/auth/create/Step2.tsx:90 +msgid "Join the waitlist." +msgstr "Uneix-te a la llista d'espera." + +#: src/view/com/modals/Waitlist.tsx:124 +msgid "Join Waitlist" +msgstr "Uneix-te a la llista d'espera" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:104 +msgid "Language selection" +msgstr "Tria l'idioma" + +#: src/view/screens/LanguageSettings.tsx:89 +msgid "Language Settings" +msgstr "Configuració d'idioma" + +#: src/view/screens/Settings.tsx:541 +msgid "Languages" +msgstr "Idiomes" + +#: src/view/com/util/moderation/ContentHider.tsx:101 +msgid "Learn more" +msgstr "Més informació" + +#: src/view/com/util/moderation/PostAlerts.tsx:47 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:65 +#: src/view/com/util/moderation/ScreenHider.tsx:104 +msgid "Learn More" +msgstr "Més informació" + +#: src/view/com/util/moderation/ContentHider.tsx:83 +#: src/view/com/util/moderation/PostAlerts.tsx:40 +#: src/view/com/util/moderation/PostHider.tsx:76 +#: src/view/com/util/moderation/ProfileHeaderAlerts.tsx:49 +#: src/view/com/util/moderation/ScreenHider.tsx:101 +msgid "Learn more about this warning" +msgstr "Més informació d'aquesta advertència" + +#: src/view/screens/Moderation.tsx:242 +msgid "Learn more about what is public on Bluesky." +msgstr "Més informació sobre què és públic a Bluesky." + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:82 +msgid "Leave them all unchecked to see any language." +msgstr "Deixa'ls tots sense marcar per veure tots els idiomes" + +#: src/view/com/modals/LinkWarning.tsx:49 +msgid "Leaving Bluesky" +msgstr "Sortint de Bluesky" + +#: src/view/com/auth/login/Login.tsx:128 +#: src/view/com/auth/login/Login.tsx:144 +msgid "Let's get your password reset!" +msgstr "Restablirem la teva contrasenya!" + +#: src/view/com/util/UserAvatar.tsx:245 +#: src/view/com/util/UserBanner.tsx:60 +msgid "Library" +msgstr "Biblioteca" + +#: src/view/screens/ProfileFeed.tsx:586 +msgid "Like this feed" +msgstr "Fes m'agrada a aquest canal" + +#: src/view/screens/PostLikedBy.tsx:27 +#: src/view/screens/ProfileFeedLikedBy.tsx:27 +msgid "Liked by" +msgstr "Li ha agradat a" + +#: src/view/screens/Profile.tsx:164 +msgid "Likes" +msgstr "M'agrades" + +#: src/view/com/modals/CreateOrEditList.tsx:186 +msgid "List Avatar" +msgstr "Avatar de la llista" + +#: src/view/com/modals/CreateOrEditList.tsx:199 +msgid "List Name" +msgstr "Nom de la llista" + +#: src/view/screens/Profile.tsx:166 +#: src/view/shell/desktop/LeftNav.tsx:379 +#: src/view/shell/Drawer.tsx:490 +#: src/view/shell/Drawer.tsx:491 +msgid "Lists" +msgstr "Llistes" + +#: src/view/com/post-thread/PostThread.tsx:259 +#: src/view/com/post-thread/PostThread.tsx:267 +msgid "Load more posts" +msgstr "Carrega més publicacions" + +#: src/view/screens/Notifications.tsx:148 +msgid "Load new notifications" +msgstr "Carrega noves notificacions" + +#: src/view/com/feeds/FeedPage.tsx:189 +msgid "Load new posts" +msgstr "Carrega noves publicacions" + +#: src/view/com/composer/text-input/mobile/Autocomplete.tsx:95 +msgid "Loading..." +msgstr "Carregant…" + +#: src/view/com/modals/ServerInput.tsx:50 +msgid "Local dev server" +msgstr "Servidor de desenvolupament local" + +#: src/view/screens/Moderation.tsx:136 +msgid "Logged-out visibility" +msgstr "Visibilitat pels usuaris no connectats" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:133 +msgid "Login to account that is not listed" +msgstr "Accedeix a un compte que no està llistat" + +#: src/view/com/modals/LinkWarning.tsx:63 +msgid "Make sure this is where you intend to go!" +msgstr "Assegura't que és aquí on vols anar!" + +#: src/view/screens/Profile.tsx:163 +msgid "Media" +msgstr "Contingut" + +#: src/view/com/threadgate/WhoCanReply.tsx:139 +msgid "mentioned users" +msgstr "usuaris mencionats" + +#: src/view/com/modals/Threadgate.tsx:93 +msgid "Mentioned users" +msgstr "Usuaris mencionats" + +#: src/view/screens/Search/Search.tsx:537 +msgid "Menu" +msgstr "Menú" + +#: src/view/com/posts/FeedErrorMessage.tsx:194 +msgid "Message from server" +msgstr "Missatge del servidor" + +#: src/view/screens/Moderation.tsx:64 +#: src/view/screens/Settings.tsx:563 +#: src/view/shell/desktop/LeftNav.tsx:397 +#: src/view/shell/Drawer.tsx:509 +#: src/view/shell/Drawer.tsx:510 +msgid "Moderation" +msgstr "Moderació" + +#: src/view/screens/Moderation.tsx:95 +msgid "Moderation lists" +msgstr "Llistes de moderació" + +#: src/view/screens/ModerationModlists.tsx:58 +msgid "Moderation Lists" +msgstr "Llistes de moderació" + +#: src/view/shell/desktop/Feeds.tsx:53 +msgid "More feeds" +msgstr "Més canals" + +#: src/view/com/profile/ProfileHeader.tsx:548 +#: src/view/screens/ProfileFeed.tsx:361 +#: src/view/screens/ProfileList.tsx:584 +msgid "More options" +msgstr "Més opcions" + +#: src/view/com/profile/ProfileHeader.tsx:370 +msgid "Mute Account" +msgstr "Silenciar el compte" + +#: src/view/screens/ProfileList.tsx:511 +msgid "Mute accounts" +msgstr "Silencia els comptes" + +#: src/view/screens/ProfileList.tsx:458 +msgid "Mute list" +msgstr "Silencia la llista" + +#: src/view/screens/ProfileList.tsx:271 +msgid "Mute these accounts?" +msgstr "Vols silenciar aquests comptes?" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:157 +msgid "Mute thread" +msgstr "Silencia el fil de discussió" + +#: src/view/screens/Moderation.tsx:109 +msgid "Muted accounts" +msgstr "Comptes silenciats" + +#: src/view/screens/ModerationMutedAccounts.tsx:107 +msgid "Muted Accounts" +msgstr "Comptes silenciats" + +#: src/view/screens/ModerationMutedAccounts.tsx:115 +msgid "Muted accounts have their posts removed from your feed and from your notifications. Mutes are completely private." +msgstr "Les publicacions dels comptes silenciats seran eliminats del teu canal i de les teves notificacions. Silenciar comptes és completament privat." + +#: src/view/screens/ProfileList.tsx:273 +msgid "Muting is private. Muted accounts can interact with you, but you will not see their posts or receive notifications from them." +msgstr "Silenciar és privat. Els comptes silenciats poden interactuar amb tu, però tu no veuràs les seves publicacions ni rebràs notificacions seves." + +#: src/view/com/modals/BirthDateSettings.tsx:56 +msgid "My Birthday" +msgstr "El meu aniversari" + +#: src/view/screens/Feeds.tsx:363 +msgid "My Feeds" +msgstr "Els meus canals" + +#: src/view/shell/desktop/LeftNav.tsx:65 +msgid "My Profile" +msgstr "El meu perfil" + +#: src/view/screens/Settings.tsx:520 +msgid "My Saved Feeds" +msgstr "Els meus canals desats" + +#: src/view/com/modals/AddAppPasswords.tsx:177 +#: src/view/com/modals/CreateOrEditList.tsx:211 +msgid "Name" +msgstr "Nom" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:72 +msgid "Never lose access to your followers and data." +msgstr "No perdis mai accés als teus seguidors ni a les teves dades." + +#: src/view/screens/Lists.tsx:76 +#: src/view/screens/ModerationModlists.tsx:78 +msgid "New" +msgstr "Nou" + +#: src/view/com/feeds/FeedPage.tsx:200 +#: src/view/screens/Feeds.tsx:507 +#: src/view/screens/Profile.tsx:354 +#: src/view/screens/ProfileFeed.tsx:431 +#: src/view/screens/ProfileList.tsx:194 +#: src/view/screens/ProfileList.tsx:222 +#: src/view/shell/desktop/LeftNav.tsx:248 +msgid "New post" +msgstr "Nova publicació" + +#: src/view/shell/desktop/LeftNav.tsx:258 +msgid "New Post" +msgstr "Nova publicació" + +#: src/view/com/auth/create/CreateAccount.tsx:154 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:174 +#: src/view/com/auth/login/ForgotPasswordForm.tsx:184 +#: src/view/com/auth/login/LoginForm.tsx:282 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:156 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:166 +#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:79 +msgid "Next" +msgstr "Següent" + +#: src/view/com/lightbox/Lightbox.web.tsx:142 +msgid "Next image" +msgstr "Següent imatge" + +#: src/view/screens/PreferencesHomeFeed.tsx:120 +#: src/view/screens/PreferencesHomeFeed.tsx:191 +#: src/view/screens/PreferencesHomeFeed.tsx:226 +#: src/view/screens/PreferencesHomeFeed.tsx:263 +msgid "No" +msgstr "No" + +#: src/view/screens/ProfileFeed.tsx:579 +#: src/view/screens/ProfileList.tsx:720 +msgid "No description" +msgstr "Cap descripció" + +#: src/view/com/composer/text-input/mobile/Autocomplete.tsx:97 +msgid "No result" +msgstr "Cap resultat" + +#: src/view/screens/Feeds.tsx:452 +msgid "No results found for \"{query}\"" +msgstr "No s'han trobat resultats per \"{query}\"" + +#: src/view/com/modals/ListAddRemoveUsers.tsx:127 +#: src/view/screens/Search/Search.tsx:270 +#: src/view/screens/Search/Search.tsx:298 +#: src/view/screens/Search/Search.tsx:629 +#: src/view/shell/desktop/Search.tsx:210 +msgid "No results found for {query}" +msgstr "No s'han trobat resultats per {query}" + +#: src/view/com/modals/Threadgate.tsx:82 +msgid "Nobody" +msgstr "Ningú" + +#: src/view/com/modals/SelfLabel.tsx:135 +msgid "Not Applicable." +msgstr "No aplicable." + +#: src/view/screens/Moderation.tsx:232 +msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites." +msgstr "Nota: Bluesky és una xarxa oberta i pública. Aquesta configuració tan sols limita el teu contingut a l'aplicació de Bluesky i a la web, altres aplicacions poden no respectar-ho. El teu contingut pot ser mostrat a usuaris no connectats per altres aplicacions i webs." + +#: src/view/screens/Notifications.tsx:113 +#: src/view/screens/Notifications.tsx:137 +#: src/view/shell/bottom-bar/BottomBar.tsx:205 +#: src/view/shell/desktop/LeftNav.tsx:361 +#: src/view/shell/Drawer.tsx:435 +#: src/view/shell/Drawer.tsx:436 +msgid "Notifications" +msgstr "Notificacions" + +#: src/view/com/util/ErrorBoundary.tsx:34 +msgid "Oh no!" +msgstr "Ostres!" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:41 +msgid "Okay" +msgstr "D'acord" + +#: src/view/com/composer/Composer.tsx:358 +msgid "One or more images is missing alt text." +msgstr "Falta el text alternatiu a una o més imatges." + +#: src/view/com/threadgate/WhoCanReply.tsx:100 +msgid "Only {0} can reply." +msgstr "Només {0} pot respondre." + +#: src/view/com/pager/FeedsTabBarMobile.tsx:76 +msgid "Open navigation" +msgstr "Obre la navegació" + +#: src/view/screens/Settings.tsx:533 +msgid "Opens configurable language settings" +msgstr "Obre les configuracions d'idioma" + +#: src/view/shell/desktop/RightNav.tsx:156 +#: src/view/shell/Drawer.tsx:641 +msgid "Opens list of invite codes" +msgstr "Obre la llista de codis d'invitació" + +#: src/view/com/modals/ChangeHandle.tsx:279 +msgid "Opens modal for using custom domain" +msgstr "Obre el modal per a utilitzar un domini personalitzat" + +#: src/view/screens/Settings.tsx:558 +msgid "Opens moderation settings" +msgstr "Obre la configuració de la moderació" + +#: src/view/screens/Settings.tsx:514 +msgid "Opens screen with all saved feeds" +msgstr "Obre la pantalla amb tots els canals desats" + +#: src/view/screens/Settings.tsx:581 +msgid "Opens the app password settings page" +msgstr "Obre la pàgina de configuració de les contrasenyes d'aplicació" + +#: src/view/screens/Settings.tsx:473 +msgid "Opens the home feed preferences" +msgstr "Obre les preferències de canals de l'inici" + +#: src/view/screens/Settings.tsx:664 +msgid "Opens the storybook page" +msgstr "Obre la pàgina de l'historial" + +#: src/view/screens/Settings.tsx:644 +msgid "Opens the system log page" +msgstr "Obre la pàgina de registres del sistema" + +#: src/view/screens/Settings.tsx:494 +msgid "Opens the threads preferences" +msgstr "Obre les preferències dels fils de discussió" + +#: src/view/com/modals/Threadgate.tsx:89 +msgid "Or combine these options:" +msgstr "O combina aquestes opcions:" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:138 +msgid "Other account" +msgstr "Un altre compte" + +#: src/view/com/modals/ServerInput.tsx:88 +msgid "Other service" +msgstr "Un altre servei" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:91 +msgid "Other..." +msgstr "Un altre…" + +#: src/view/screens/NotFound.tsx:42 +#: src/view/screens/NotFound.tsx:45 +msgid "Page not found" +msgstr "Pàgina no trobada" + +#: src/view/com/auth/create/Step2.tsx:122 +#: src/view/com/auth/create/Step2.tsx:132 +#: src/view/com/auth/login/LoginForm.tsx:217 +#: src/view/com/auth/login/SetNewPasswordForm.tsx:130 +#: src/view/com/modals/DeleteAccount.tsx:191 +msgid "Password" +msgstr "Contrasenya" + +#: src/view/com/auth/login/Login.tsx:157 +msgid "Password updated" +msgstr "Contrasenya actualitzada" + +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:28 +msgid "Password updated!" +msgstr "Contrasenya actualitzada!" + +#: src/view/com/modals/SelfLabel.tsx:121 +msgid "Pictures meant for adults." +msgstr "Imatges destinades a adults." + +#: src/view/screens/SavedFeeds.tsx:88 +msgid "Pinned Feeds" +msgstr "Canals de notícies ancorats" + +#: src/view/com/auth/create/state.ts:116 +msgid "Please choose your handle." +msgstr "Tria el teu identificador." + +#: src/view/com/auth/create/state.ts:109 +msgid "Please choose your password." +msgstr "Tria la teva contrasenya." + +#: 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 "Confirma el teu correu abans de canviar-lo. Aquest és un requisit temporal mentre no s'afegeixin eines per actualitzar el correu. Aviat no serà necessari," + +#: src/view/com/modals/AddAppPasswords.tsx:140 +msgid "Please enter a unique name for this App Password or use our randomly generated one." +msgstr "Introdueix un nom únic per aquesta contrasenya d'aplicació o fes servir un nom generat aleatòriament." + +#: src/view/com/auth/create/state.ts:95 +msgid "Please enter your email." +msgstr "Introdueix el teu correu." + +#: src/view/com/modals/DeleteAccount.tsx:180 +msgid "Please enter your password as well:" +msgstr "Introdueix la teva contrasenya també:" + +#: src/view/com/modals/AppealLabel.tsx:72 +#: src/view/com/modals/AppealLabel.tsx:75 +msgid "Please tell us why you think this content warning was incorrectly applied!" +msgstr "Digues-nos per què creus que s'ha aplicat incorrectament l'advertència de contingut." + +#: src/view/com/composer/Composer.tsx:214 +msgid "Please wait for your link card to finish loading" +msgstr "Espera que es generi la targeta de l'enllaç" + +#: src/view/com/composer/Composer.tsx:341 +#: src/view/com/post-thread/PostThread.tsx:225 +#: src/view/screens/PostThread.tsx:80 +msgid "Post" +msgstr "Publicació" + +#: src/view/com/post-thread/PostThread.tsx:378 +msgid "Post hidden" +msgstr "Publicació oculta" + +#: src/view/com/composer/select-language/SelectLangBtn.tsx:87 +msgid "Post language" +msgstr "Idioma de la publicació" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:75 +msgid "Post Languages" +msgstr "Idiomes de les publicacions" + +#: src/view/com/post-thread/PostThread.tsx:430 +msgid "Post not found" +msgstr "Publicació no trobada" + +#: src/view/screens/Profile.tsx:161 +msgid "Posts" +msgstr "Publicacions" + +#: src/view/com/modals/LinkWarning.tsx:44 +msgid "Potentially Misleading Link" +msgstr "Enllaç potencialment enganyós" + +#: src/view/com/lightbox/Lightbox.web.tsx:128 +msgid "Previous image" +msgstr "Imatge anterior" + +#: src/view/screens/LanguageSettings.tsx:187 +msgid "Primary Language" +msgstr "Idioma principal" + +#: src/view/screens/PreferencesThreads.tsx:91 +msgid "Prioritize Your Follows" +msgstr "Prioritza els usuaris que segueixes" + +#: src/view/shell/desktop/RightNav.tsx:84 +msgid "Privacy" +msgstr "Privacitat" + +#: src/view/screens/PrivacyPolicy.tsx:29 +#: src/view/screens/Settings.tsx:750 +#: src/view/shell/Drawer.tsx:262 +msgid "Privacy Policy" +msgstr "Política de privacitat" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:190 +msgid "Processing..." +msgstr "Processant…" + +#: src/view/shell/bottom-bar/BottomBar.tsx:247 +#: src/view/shell/desktop/LeftNav.tsx:415 +#: src/view/shell/Drawer.tsx:70 +#: src/view/shell/Drawer.tsx:544 +#: src/view/shell/Drawer.tsx:545 +msgid "Profile" +msgstr "Perfil" + +#: src/view/screens/Settings.tsx:808 +msgid "Protect your account by verifying your email." +msgstr "Protegeix el teu compte verificant el teu correu." + +#: src/view/screens/ModerationModlists.tsx:61 +msgid "Public, shareable lists of users to mute or block in bulk." +msgstr "Llistes d'usuaris per silenciar o bloquejar en massa, públiques i per compartir." + +#: src/view/screens/Lists.tsx:61 +msgid "Public, shareable lists which can drive feeds." +msgstr "Llistes que poden nodrir canals, públiques i per compartir." + +#: src/view/com/modals/Repost.tsx:52 +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:58 +msgid "Quote post" +msgstr "Cita una publicació" + +#: src/view/com/modals/Repost.tsx:56 +msgid "Quote Post" +msgstr "Cita una publicació" + +#: src/view/com/modals/EditImage.tsx:236 +msgid "Ratios" +msgstr "Proporcions" + +#: src/view/com/auth/onboarding/RecommendedFeeds.tsx:116 +msgid "Recommended Feeds" +msgstr "Canals recomanats" + +#: src/view/com/auth/onboarding/RecommendedFollows.tsx:180 +msgid "Recommended Users" +msgstr "Usuaris recomanats" + +#: src/view/com/modals/ListAddRemoveUsers.tsx:264 +#: src/view/com/modals/SelfLabel.tsx:83 +#: src/view/com/modals/UserAddRemoveLists.tsx:193 +#: src/view/com/util/UserAvatar.tsx:282 +#: src/view/com/util/UserBanner.tsx:89 +msgid "Remove" +msgstr "Elimina" + +#: src/view/com/feeds/FeedSourceCard.tsx:106 +msgid "Remove {0} from my feeds?" +msgstr "Vols eliminar {0} dels teus canals?" + +#: src/view/com/util/AccountDropdownBtn.tsx:22 +msgid "Remove account" +msgstr "Elimina el compte" + +#: src/view/com/posts/FeedErrorMessage.tsx:130 +msgid "Remove feed" +msgstr "Elimina el canal" + +#: src/view/com/feeds/FeedSourceCard.tsx:105 +#: src/view/com/feeds/FeedSourceCard.tsx:172 +#: src/view/screens/ProfileFeed.tsx:271 +msgid "Remove from my feeds" +msgstr "Elimina dels meus canals" + +#: src/view/com/composer/photos/Gallery.tsx:167 +msgid "Remove image" +msgstr "Elimina la imatge" + +#: src/view/com/composer/ExternalEmbed.tsx:70 +msgid "Remove image preview" +msgstr "Elimina la visualització prèvia de la imatge" + +#: src/view/com/feeds/FeedSourceCard.tsx:173 +msgid "Remove this feed from my feeds?" +msgstr "Vols eliminar aquest canal dels meus canals?" + +#: src/view/com/posts/FeedErrorMessage.tsx:131 +msgid "Remove this feed from your saved feeds?" +msgstr "Vols eliminar aquest canal dels teus canals desats?" + +#: src/view/com/modals/ListAddRemoveUsers.tsx:199 +#: src/view/com/modals/UserAddRemoveLists.tsx:136 +msgid "Removed from list" +msgstr "Elimina de la llista" + +#: src/view/screens/Profile.tsx:162 +msgid "Replies" +msgstr "Respostes" + +#: src/view/com/threadgate/WhoCanReply.tsx:98 +msgid "Replies to this thread are disabled" +msgstr "Les respostes a aquest fil de discussió estan deshabilitades" + +#: src/view/screens/PreferencesHomeFeed.tsx:135 +msgid "Reply Filters" +msgstr "Filtres de resposta" + +#: src/view/com/modals/report/Modal.tsx:166 +msgid "Report {collectionName}" +msgstr "Informa de {collectionName}" + +#: src/view/com/profile/ProfileHeader.tsx:404 +msgid "Report Account" +msgstr "Informa del compte" + +#: src/view/screens/ProfileFeed.tsx:291 +msgid "Report feed" +msgstr "Informa del canal" + +#: src/view/screens/ProfileList.tsx:426 +msgid "Report List" +msgstr "Informa de la llista" + +#: src/view/com/modals/report/SendReportButton.tsx:37 +#: src/view/com/util/forms/PostDropdownBtn.tsx:196 +msgid "Report post" +msgstr "Informa de la publicació" + +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:48 +msgid "Repost" +msgstr "Republica" + +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:94 +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:105 +msgid "Repost or quote post" +msgstr "Republica o cita la publicació" + +#: src/view/screens/PostRepostedBy.tsx:27 +msgid "Reposted by" +msgstr "Republicada per" + +#: src/view/com/modals/ChangeEmail.tsx:181 +#: src/view/com/modals/ChangeEmail.tsx:183 +msgid "Request Change" +msgstr "Demana un canvi" + +#: src/view/com/auth/create/Step2.tsx:68 +msgid "Required for this provider" +msgstr "Requerit per aquest proveïdor" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:108 +msgid "Reset code" +msgstr "Codi de restabliment" + +#: src/view/screens/Settings.tsx:686 +msgid "Reset onboarding state" +msgstr "Restableix l'estat de la incorporació" + +#: src/view/com/auth/login/ForgotPasswordForm.tsx:98 +msgid "Reset password" +msgstr "Restableix la contrasenya" + +#: src/view/screens/Settings.tsx:676 +msgid "Reset preferences state" +msgstr "Restableix l'estat de les preferències" + +#: src/view/screens/Settings.tsx:684 +msgid "Resets the onboarding state" +msgstr "Restableix l'estat de la incorporació" + +#: src/view/screens/Settings.tsx:674 +msgid "Resets the preferences state" +msgstr "Restableix l'estat de les preferències" + +#: src/view/com/auth/create/CreateAccount.tsx:163 +#: src/view/com/auth/create/CreateAccount.tsx:167 +#: src/view/com/auth/login/LoginForm.tsx:259 +#: src/view/com/auth/login/LoginForm.tsx:262 +#: src/view/com/util/error/ErrorMessage.tsx:55 +#: src/view/com/util/error/ErrorScreen.tsx:65 +msgid "Retry" +msgstr "Torna-ho a provar" + +#: src/view/com/modals/AltImage.tsx:115 +#: src/view/com/modals/BirthDateSettings.tsx:94 +#: src/view/com/modals/BirthDateSettings.tsx:97 +#: src/view/com/modals/ChangeHandle.tsx:173 +#: src/view/com/modals/CreateOrEditList.tsx:249 +#: src/view/com/modals/CreateOrEditList.tsx:257 +#: src/view/com/modals/EditProfile.tsx:223 +msgid "Save" +msgstr "Desa" + +#: src/view/com/modals/AltImage.tsx:106 +msgid "Save alt text" +msgstr "Desa el text alternatiu" + +#: src/view/com/modals/EditProfile.tsx:231 +msgid "Save Changes" +msgstr "Desa els canvis" + +#: src/view/com/modals/ChangeHandle.tsx:170 +msgid "Save handle change" +msgstr "Desa el canvi d'identificador" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:144 +msgid "Save image crop" +msgstr "Desa la imatge retallada" + +#: src/view/screens/SavedFeeds.tsx:122 +msgid "Saved Feeds" +msgstr "Canals desats" + +#: src/view/com/modals/ListAddRemoveUsers.tsx:75 +#: src/view/com/util/forms/SearchInput.tsx:65 +#: src/view/screens/Search/Search.tsx:406 +#: src/view/screens/Search/Search.tsx:572 +#: src/view/shell/bottom-bar/BottomBar.tsx:159 +#: src/view/shell/desktop/LeftNav.tsx:324 +#: src/view/shell/desktop/Search.tsx:161 +#: src/view/shell/desktop/Search.tsx:170 +#: src/view/shell/Drawer.tsx:362 +#: src/view/shell/Drawer.tsx:363 +msgid "Search" +msgstr "Cerca" + +#: src/view/com/auth/LoggedOut.tsx:104 +#: src/view/com/auth/LoggedOut.tsx:105 +msgid "Search for users" +msgstr "Cerca usuaris" + +#: src/view/com/modals/ChangeEmail.tsx:110 +msgid "Security Step Required" +msgstr "Es requereix un pas de seguretat" + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:39 +msgid "See what's next" +msgstr "Què més hi ha" + +#: src/view/com/modals/ServerInput.tsx:75 +msgid "Select Bluesky Social" +msgstr "Selecciona Bluesky Social" + +#: src/view/com/auth/login/Login.tsx:117 +msgid "Select from an existing account" +msgstr "Selecciona d'un compte existent" + +#: src/view/com/auth/login/LoginForm.tsx:143 +msgid "Select service" +msgstr "Selecciona el servei" + +#: src/view/screens/LanguageSettings.tsx:281 +msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown." +msgstr "Selecciona quins idiomes vols que incloguin els canals a què estàs subscrit. Si no en selecciones cap, es mostraran tots." + +#: src/view/screens/LanguageSettings.tsx:98 +msgid "Select your app language for the default text to display in the app" +msgstr "Seleccioneu l'idioma de l'aplicació perquè el text predeterminat es mostri en aquesta" + +#: src/view/screens/LanguageSettings.tsx:190 +msgid "Select your preferred language for translations in your feed." +msgstr "Selecciona el teu idioma preferit per a les traduccions al teu canal." + +#: src/view/com/modals/VerifyEmail.tsx:196 +msgid "Send Confirmation Email" +msgstr "Envia correu de confirmació" + +#: src/view/com/modals/DeleteAccount.tsx:127 +msgid "Send email" +msgstr "Envia correu" + +#: src/view/com/modals/DeleteAccount.tsx:138 +msgid "Send Email" +msgstr "Envia correu" + +#: src/view/shell/Drawer.tsx:295 +#: src/view/shell/Drawer.tsx:316 +msgid "Send feedback" +msgstr "Envia comentari" + +#: src/view/com/modals/report/SendReportButton.tsx:45 +msgid "Send Report" +msgstr "Envia informe" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:78 +msgid "Set new password" +msgstr "Estableix una nova contrasenya" + +#: src/view/screens/PreferencesHomeFeed.tsx:216 +msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." +msgstr "Posa \"No\" a aquesta opció per amagar totes les publicacions citades del teu canal. Les republicacions encara seran visibles." + +#: src/view/screens/PreferencesHomeFeed.tsx:113 +msgid "Set this setting to \"No\" to hide all replies from your feed." +msgstr "Posa \"No\" a aquesta opció per amagar totes les respostes del teu canal." + +#: src/view/screens/PreferencesHomeFeed.tsx:182 +msgid "Set this setting to \"No\" to hide all reposts from your feed." +msgstr "Posa \"No\" a aquesta opció per amagar totes les republicacions del teu canal." + +#: src/view/screens/PreferencesThreads.tsx:116 +msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." +msgstr "Posa \"Sí\" a aquesta opció per mostrar les respostes en vista de fil de discussió. Aquesta és una opció experimental." + +#: src/view/screens/PreferencesHomeFeed.tsx:252 +msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." +msgstr "Posa \"Sí\" a aquesta opció per mostrar algunes publicacions dels teus canals en el teu canal de seguits. Aquesta és una opció experimental." + +#: src/view/screens/Settings.tsx:277 +#: src/view/shell/desktop/LeftNav.tsx:433 +#: src/view/shell/Drawer.tsx:565 +#: src/view/shell/Drawer.tsx:566 +msgid "Settings" +msgstr "Configuracions" + +#: src/view/com/modals/SelfLabel.tsx:125 +msgid "Sexual activity or erotic nudity." +msgstr "Activitat sexual o nu eròtic." + +#: src/view/com/profile/ProfileHeader.tsx:338 +#: src/view/com/util/forms/PostDropdownBtn.tsx:139 +#: src/view/screens/ProfileList.tsx:385 +msgid "Share" +msgstr "Comparteix" + +#: src/view/screens/ProfileFeed.tsx:303 +msgid "Share feed" +msgstr "Comparteix el canal" + +#: src/view/com/util/moderation/ContentHider.tsx:105 +#: src/view/screens/Settings.tsx:316 +msgid "Show" +msgstr "Mostra" + +#: src/view/com/util/moderation/ScreenHider.tsx:132 +msgid "Show anyway" +msgstr "Mostra igualment" + +#: src/view/screens/PreferencesHomeFeed.tsx:249 +msgid "Show Posts from My Feeds" +msgstr "Mostra les publicacions dels meus canals" + +#: src/view/screens/PreferencesHomeFeed.tsx:213 +msgid "Show Quote Posts" +msgstr "Mostra les publicacions citades" + +#: src/view/screens/PreferencesHomeFeed.tsx:110 +msgid "Show Replies" +msgstr "Mostra les respostes" + +#: src/view/screens/PreferencesThreads.tsx:94 +msgid "Show replies by people you follow before all other replies." +msgstr "Mostra les respostes dels comptes que segueixes abans que les altres." + +#: src/view/screens/PreferencesHomeFeed.tsx:179 +msgid "Show Reposts" +msgstr "Mostra republicacions" + +#: src/view/com/notifications/FeedItem.tsx:345 +msgid "Show users" +msgstr "Mostra usuaris" + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:70 +#: src/view/com/auth/login/Login.tsx:98 +#: src/view/com/auth/SplashScreen.tsx:54 +#: src/view/shell/bottom-bar/BottomBar.tsx:285 +#: src/view/shell/bottom-bar/BottomBar.tsx:286 +#: src/view/shell/bottom-bar/BottomBar.tsx:288 +#: src/view/shell/bottom-bar/BottomBarWeb.tsx:177 +#: src/view/shell/bottom-bar/BottomBarWeb.tsx:178 +#: src/view/shell/bottom-bar/BottomBarWeb.tsx:180 +#: src/view/shell/NavSignupCard.tsx:58 +#: src/view/shell/NavSignupCard.tsx:59 +msgid "Sign in" +msgstr "Inicia sessió" + +#: src/view/com/auth/HomeLoggedOutCTA.tsx:78 +#: src/view/com/auth/SplashScreen.tsx:57 +#: src/view/com/auth/SplashScreen.web.tsx:87 +msgid "Sign In" +msgstr "Inicia sessió" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:44 +msgid "Sign in as {0}" +msgstr "Inicia sessió com a {0}" + +#: src/view/com/auth/login/ChooseAccountForm.tsx:118 +#: src/view/com/auth/login/Login.tsx:116 +msgid "Sign in as..." +msgstr "Inicia sessió com a …" + +#: src/view/com/auth/login/LoginForm.tsx:130 +msgid "Sign into" +msgstr "Inicia sessió en" + +#: src/view/com/modals/SwitchAccount.tsx:64 +#: src/view/com/modals/SwitchAccount.tsx:67 +msgid "Sign out" +msgstr "Tanca sessió" + +#: src/view/shell/bottom-bar/BottomBar.tsx:275 +#: src/view/shell/bottom-bar/BottomBar.tsx:276 +#: src/view/shell/bottom-bar/BottomBar.tsx:278 +#: src/view/shell/bottom-bar/BottomBarWeb.tsx:167 +#: src/view/shell/bottom-bar/BottomBarWeb.tsx:168 +#: src/view/shell/bottom-bar/BottomBarWeb.tsx:170 +#: src/view/shell/NavSignupCard.tsx:49 +#: src/view/shell/NavSignupCard.tsx:50 +#: src/view/shell/NavSignupCard.tsx:52 +msgid "Sign up" +msgstr "Registra't" + +#: src/view/shell/NavSignupCard.tsx:42 +msgid "Sign up or sign in to join the conversation" +msgstr "Registra't o inicia sessió per unir-te a la conversa" + +#: src/view/com/util/moderation/ScreenHider.tsx:76 +msgid "Sign-in Required" +msgstr "Es requereix iniciar sessió" + +#: src/view/screens/Settings.tsx:327 +msgid "Signed in as" +msgstr "S'ha iniciat sessió com a" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:33 +msgid "Skip" +msgstr "Salta aquest pas" + +#: src/view/screens/PreferencesThreads.tsx:69 +msgid "Sort Replies" +msgstr "Ordena les respostes" + +#: src/view/screens/PreferencesThreads.tsx:72 +msgid "Sort replies to the same post by:" +msgstr "Ordena les respostes a la mateixa publicació per:" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:122 +msgid "Square" +msgstr "Quadrat" + +#: src/view/com/auth/create/Step1.tsx:90 +#: src/view/com/modals/ServerInput.tsx:62 +msgid "Staging" +msgstr "Posada en escena" + +#: src/view/screens/Settings.tsx:730 +msgid "Status page" +msgstr "Pàgina d'estat" + +#: src/view/screens/Settings.tsx:666 +msgid "Storybook" +msgstr "Historial" + +#: src/view/com/modals/AppealLabel.tsx:101 +msgid "Submit" +msgstr "Envia" + +#: src/view/screens/ProfileList.tsx:575 +msgid "Subscribe" +msgstr "Subscriure's" + +#: src/view/screens/ProfileList.tsx:571 +msgid "Subscribe to this list" +msgstr "Subscriure's a la llista" + +#: src/view/screens/Search/Search.tsx:362 +msgid "Suggested Follows" +msgstr "Usuaris suggerits per seguir" + +#: src/view/screens/Support.tsx:30 +#: src/view/screens/Support.tsx:33 +msgid "Support" +msgstr "Suport" + +#: src/view/com/modals/SwitchAccount.tsx:115 +msgid "Switch Account" +msgstr "Canvia el compte" + +#: src/view/screens/Settings.tsx:646 +msgid "System log" +msgstr "Registres del sistema" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:112 +msgid "Tall" +msgstr "Alt" + +#: src/view/shell/desktop/RightNav.tsx:93 +msgid "Terms" +msgstr "Condicions" + +#: src/view/screens/Settings.tsx:744 +#: src/view/screens/TermsOfService.tsx:29 +#: src/view/shell/Drawer.tsx:256 +msgid "Terms of Service" +msgstr "Condicions del servei" + +#: src/view/com/modals/AppealLabel.tsx:70 +#: src/view/com/modals/report/InputIssueDetails.tsx:51 +msgid "Text input field" +msgstr "Camp d'introducció de text" + +#: src/view/com/profile/ProfileHeader.tsx:306 +msgid "The account will be able to interact with you after unblocking." +msgstr "El compte podrà interactuar amb tu després del desbloqueig." + +#: src/view/screens/CommunityGuidelines.tsx:36 +msgid "The Community Guidelines have been moved to <0/>" +msgstr "Les directrius de la comunitat han estat traslladades a <0/>" + +#: src/view/screens/CopyrightPolicy.tsx:33 +msgid "The Copyright Policy has been moved to <0/>" +msgstr "La política de drets d'autoria ha estat traslladada a <0/>" + +#: src/view/com/post-thread/PostThread.tsx:433 +msgid "The post may have been deleted." +msgstr "És possible que la publicació s'hagi esborrat." + +#: src/view/screens/PrivacyPolicy.tsx:33 +msgid "The Privacy Policy has been moved to <0/>" +msgstr "La política de privacitat ha estat traslladada a <0/>" + +#: src/view/screens/Support.tsx:36 +msgid "The support form has been moved. If you need help, please<0/> or visit {HELP_DESK_URL} to get in touch with us." +msgstr "El formulari de suport ha estat traslladat. Si necessites ajuda, <0/> o visita {HELP_DESK_URL} per contactar amb nosaltres." + +#: src/view/screens/TermsOfService.tsx:33 +msgid "The Terms of Service have been moved to" +msgstr "Les condicions del servei han estat traslladades a " + +#: src/view/com/util/ErrorBoundary.tsx:35 +msgid "There was an unexpected issue in the application. Please let us know if this happened to you!" +msgstr "S'ha produït un problema inesperat a l'aplicació. Fes-nos saber si això t'ha passat a tu!" + +#: src/view/com/util/moderation/ScreenHider.tsx:88 +msgid "This {screenDescription} has been flagged:" +msgstr "Aquesta {screenDescription} ha estat etiquetada:" + +#: src/view/com/util/moderation/ScreenHider.tsx:83 +msgid "This account has requested that users sign in to view their profile." +msgstr "Aquest compte ha sol·licitat que els usuaris estiguin registrats per veure el seu perfil." + +#: src/view/com/posts/FeedErrorMessage.tsx:107 +msgid "This content is not viewable without a Bluesky account." +msgstr "Aquest contingut no es pot veure sense un compte de Bluesky." + +#: src/view/com/posts/FeedErrorMessage.tsx:113 +msgid "This feed is currently receiving high traffic and is temporarily unavailable. Please try again later." +msgstr "Aquest canal està rebent moltes visites actualment i està temporalment inactiu. Prova-ho més tard." + +#: src/view/com/modals/BirthDateSettings.tsx:61 +msgid "This information is not shared with other users." +msgstr "Aquesta informació no es comparteix amb altres usuaris." + +#: src/view/com/modals/VerifyEmail.tsx:113 +msgid "This is important in case you ever need to change your email or reset your password." +msgstr "Això és important si mai necessites canviar el teu correu o restablir la contrasenya." + +#: src/view/com/auth/create/Step1.tsx:55 +msgid "This is the service that keeps you online." +msgstr "Aquest és el servei que et manté connectat." + +#: src/view/com/modals/LinkWarning.tsx:56 +msgid "This link is taking you to the following website:" +msgstr "Aquest enllaç et porta a la web:" + +#: src/view/com/post-thread/PostThreadItem.tsx:123 +msgid "This post has been deleted." +msgstr "Aquesta publicació ha estat esborrada." + +#: src/view/com/modals/SelfLabel.tsx:137 +msgid "This warning is only available for posts with media attached." +msgstr "Aquesta advertència només està disponible per publicacions amb contingut adjuntat." + +#: src/view/com/util/forms/PostDropdownBtn.tsx:178 +msgid "This will hide this post from your feeds." +msgstr "Això amagarà aquesta publicació dels teus canals" + +#: src/view/screens/PreferencesThreads.tsx:53 +#: src/view/screens/Settings.tsx:503 +msgid "Thread Preferences" +msgstr "Preferències dels fils de discussió" + +#: src/view/screens/PreferencesThreads.tsx:113 +msgid "Threaded Mode" +msgstr "Mode fils de discussió" + +#: src/view/com/util/forms/DropdownButton.tsx:230 +msgid "Toggle dropdown" +msgstr "Commuta el menú desplegable" + +#: src/view/com/modals/EditImage.tsx:271 +msgid "Transformations" +msgstr "Transformacions" + +#: src/view/com/post-thread/PostThreadItem.tsx:705 +#: src/view/com/post-thread/PostThreadItem.tsx:707 +#: src/view/com/util/forms/PostDropdownBtn.tsx:111 +msgid "Translate" +msgstr "Tradueix" + +#: src/view/com/util/error/ErrorScreen.tsx:73 +msgid "Try again" +msgstr "Torna-ho a provar" + +#: src/view/screens/ProfileList.tsx:473 +msgid "Un-block list" +msgstr "Desbloqueja la llista" + +#: src/view/screens/ProfileList.tsx:458 +msgid "Un-mute list" +msgstr "Deixa de silenciar la llista" + +#: src/view/com/auth/create/CreateAccount.tsx:65 +#: src/view/com/auth/login/Login.tsx:76 +#: src/view/com/auth/login/LoginForm.tsx:117 +msgid "Unable to contact your service. Please check your Internet connection." +msgstr "No es pot contactar amb el teu servei. Comprova la teva connexió a internet." + +#: src/view/com/profile/ProfileHeader.tsx:466 +#: src/view/com/profile/ProfileHeader.tsx:469 +msgid "Unblock" +msgstr "Desbloqueja" + +#: src/view/com/profile/ProfileHeader.tsx:304 +#: src/view/com/profile/ProfileHeader.tsx:388 +msgid "Unblock Account" +msgstr "Desbloqueja el compte" + +#: src/view/com/util/post-ctrls/RepostButton.web.tsx:48 +msgid "Undo repost" +msgstr "Desfés la republicació" + +#: src/view/com/auth/create/state.ts:210 +msgid "Unfortunately, you do not meet the requirements to create an account." +msgstr "No compleixes les condicions per crear un compte." + +#: src/view/com/profile/ProfileHeader.tsx:369 +msgid "Unmute Account" +msgstr "Deixa de silenciar el compte" + +#: src/view/com/util/forms/PostDropdownBtn.tsx:157 +msgid "Unmute thread" +msgstr "Deixa de silenciar el fil de discussió" + +#: src/view/screens/ProfileList.tsx:441 +msgid "Unpin moderation list" +msgstr "Desancora la llista de moderació" + +#: src/view/com/modals/UserAddRemoveLists.tsx:54 +msgid "Update {displayName} in Lists" +msgstr "Actualitza {displayName} a les Llistes" + +#: src/lib/hooks/useOTAUpdate.ts:15 +msgid "Update Available" +msgstr "Actualització disponible" + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:172 +msgid "Updating..." +msgstr "Actualitzant…" + +#: src/view/com/modals/ChangeHandle.tsx:453 +msgid "Upload a text file to:" +msgstr "Puja un fitxer de text a:" + +#: src/view/screens/AppPasswords.tsx:194 +msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." +msgstr "Utilitza les contrasenyes d'aplicació per iniciar sessió en altres clients de Bluesky, sense haver de donar accés total al teu compte o contrasenya." + +#: src/view/com/modals/ChangeHandle.tsx:513 +msgid "Use default provider" +msgstr "Utilitza el proveïdor predeterminat" + +#: src/view/com/modals/AddAppPasswords.tsx:150 +msgid "Use this to sign into the other app along with your handle." +msgstr "Utilitza-ho per iniciar sessió a l'altra aplicació, juntament amb el teu identificador." + +#: src/view/com/modals/InviteCodes.tsx:197 +msgid "Used by:" +msgstr "Utilitzat per:" + +#: src/view/com/auth/create/Step3.tsx:38 +msgid "User handle" +msgstr "Identificador d'usuari" + +#: src/view/screens/Lists.tsx:58 +msgid "User Lists" +msgstr "Llistes d'usuaris" + +#: src/view/com/auth/login/LoginForm.tsx:170 +#: src/view/com/auth/login/LoginForm.tsx:188 +msgid "Username or email address" +msgstr "Nom d'usuari o correu" + +#: src/view/screens/ProfileList.tsx:747 +msgid "Users" +msgstr "Usuaris" + +#: src/view/com/threadgate/WhoCanReply.tsx:143 +msgid "users followed by <0/>" +msgstr "usuaris seguits per <0/>" + +#: src/view/com/modals/Threadgate.tsx:106 +msgid "Users in \"{0}\"" +msgstr "Usuaris a «{0}»" + +#: src/view/screens/Settings.tsx:769 +msgid "Verify email" +msgstr "Verifica el correu" + +#: src/view/screens/Settings.tsx:794 +msgid "Verify my email" +msgstr "Verifica el meu correu" + +#: src/view/screens/Settings.tsx:803 +msgid "Verify My Email" +msgstr "Verifica el meu correu" + +#: src/view/com/modals/ChangeEmail.tsx:205 +#: src/view/com/modals/ChangeEmail.tsx:207 +msgid "Verify New Email" +msgstr "Verifica el correu nou" + +#: src/view/screens/Log.tsx:52 +msgid "View debug entry" +msgstr "Veure el registre de depuració" + +#: src/view/com/profile/ProfileSubpageHeader.tsx:128 +msgid "View the avatar" +msgstr "Veure l'avatar" + +#: src/view/com/modals/LinkWarning.tsx:73 +msgid "Visit Site" +msgstr "Visita el lloc web" + +#: src/view/com/auth/create/CreateAccount.tsx:122 +msgid "We're so excited to have you join us!" +msgstr "Ens fa molta il·lusió que t'uneixis a nosaltres!" + +#: src/view/screens/Search/Search.tsx:243 +msgid "We're sorry, but your search could not be completed. Please try again in a few minutes." +msgstr "Ens sap greu, però la teva cerca no s'ha pogut fer. Prova-ho d'aquí una estona." + +#: src/view/screens/NotFound.tsx:48 +msgid "We're sorry! We can't find the page you were looking for." +msgstr "Ens sap greu! No podem trobar la pàgina que estàs cercant." + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:46 +msgid "Welcome to <0>Bluesky" +msgstr "Benvingut a <0>Bluesky" + +#: src/view/com/modals/report/Modal.tsx:169 +msgid "What is the issue with this {collectionName}?" +msgstr "Quin problema hi ha amb {collectionName}?" + +#: src/view/com/auth/SplashScreen.tsx:34 +msgid "What's up?" +msgstr "Què hi ha de nou" + +#: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:78 +msgid "Which languages are used in this post?" +msgstr "En quins idiomes està aquesta publicació?" + +#: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:77 +msgid "Which languages would you like to see in your algorithmic feeds?" +msgstr "Quins idiomes t'agradaria veure en els teus canals algorítmics?" + +#: src/view/com/composer/threadgate/ThreadgateBtn.tsx:47 +#: src/view/com/modals/Threadgate.tsx:66 +msgid "Who can reply" +msgstr "Qui hi pot respondre" + +#: src/view/com/modals/crop-image/CropImage.web.tsx:102 +msgid "Wide" +msgstr "Amplada" + +#: src/view/com/composer/Composer.tsx:413 +msgid "Write post" +msgstr "Escriu una publicació" + +#: src/view/com/composer/Prompt.tsx:33 +msgid "Write your reply" +msgstr "Escriu la teva resposta" + +#: src/view/screens/PreferencesHomeFeed.tsx:120 +#: src/view/screens/PreferencesHomeFeed.tsx:192 +#: src/view/screens/PreferencesHomeFeed.tsx:227 +#: src/view/screens/PreferencesHomeFeed.tsx:262 +msgid "Yes" +msgstr "Sí" + +#: src/view/com/auth/create/Step1.tsx:106 +msgid "You can change hosting providers at any time." +msgstr "Pots canviar el teu proveïdor d'allotjament quan vulguis." + +#: src/view/com/auth/login/Login.tsx:158 +#: src/view/com/auth/login/PasswordUpdatedForm.tsx:31 +msgid "You can now sign in with your new password." +msgstr "Ara pots iniciar sessió amb la nova contrasenya." + +#: src/view/com/modals/InviteCodes.tsx:64 +msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer." +msgstr "Encara no tens codis d'invitació! Te n'enviarem quan portis una mica més de temps a Bluesky." + +#: src/view/screens/SavedFeeds.tsx:102 +msgid "You don't have any pinned feeds." +msgstr "No tens cap canal fixat." + +#: src/view/screens/Feeds.tsx:383 +msgid "You don't have any saved feeds!" +msgstr "No tens cap canal desat!" + +#: src/view/screens/SavedFeeds.tsx:135 +msgid "You don't have any saved feeds." +msgstr "No tens cap canal desat." + +#: src/view/com/post-thread/PostThread.tsx:381 +msgid "You have blocked the author or you have been blocked by the author." +msgstr "Has bloquejat l'autor o has estat bloquejat per ell." + +#: src/view/com/feeds/ProfileFeedgens.tsx:134 +msgid "You have no feeds." +msgstr "No tens canals." + +#: src/view/com/lists/MyLists.tsx:89 +#: src/view/com/lists/ProfileLists.tsx:138 +msgid "You have no lists." +msgstr "No tens llistes." + +#: src/view/screens/ModerationBlockedAccounts.tsx:132 +msgid "You have not blocked any accounts yet. To block an account, go to their profile and selected \"Block account\" from the menu on their account." +msgstr "Encara no has bloquejat cap compte. Per fer-ho, vés al seu perfil i selecciona \"Bloqueja el compte\" en el menú del seu compte." + +#: src/view/screens/AppPasswords.tsx:86 +msgid "You have not created any app passwords yet. You can create one by pressing the button below." +msgstr "Encara no has creat cap contrasenya d'aplicació. Pots fer-ho amb el botó d'aquí sota." + +#: src/view/screens/ModerationMutedAccounts.tsx:131 +msgid "You have not muted any accounts yet. To mute an account, go to their profile and selected \"Mute account\" from the menu on their account." +msgstr "Encara no has silenciat cap compte. Per fer-ho, vés al seu perfil i selecciona \"Silencia compte\" en el menú del seu compte." + +#: src/view/com/auth/login/SetNewPasswordForm.tsx:81 +msgid "You will receive an email with a \"reset code.\" Enter that code here, then enter your new password." +msgstr "Rebràs un correu amb un \"codi de restabliment\". Introdueix aquí el codi i després la teva contrasenya nova." + +#: src/view/com/auth/create/Step2.tsx:58 +msgid "Your account" +msgstr "El teu compte" + +#: src/view/com/auth/create/Step2.tsx:146 +msgid "Your birth date" +msgstr "La teva data de naixement" + +#: src/view/com/auth/create/state.ts:102 +msgid "Your email appears to be invalid." +msgstr "El teu correu no sembla vàlid." + +#: src/view/com/modals/Waitlist.tsx:107 +msgid "Your email has been saved! We'll be in touch soon." +msgstr "Hem desat el teu correu! Aviat ens posarem en contacte amb tu." + +#: 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 "El teu correu s'ha actualitzat, però no ha estat verificat. En el pas següent cal que verifiquis el teu correu." + +#: src/view/com/modals/VerifyEmail.tsx:108 +msgid "Your email has not yet been verified. This is an important security step which we recommend." +msgstr "El teu correu encara no s'ha verificat. Et recomanem fer-ho per seguretat." + +#: src/view/com/auth/create/Step3.tsx:42 +#: src/view/com/modals/ChangeHandle.tsx:270 +msgid "Your full handle will be" +msgstr "El teu identificador complet serà" + +#: src/view/com/auth/create/Step1.tsx:53 +msgid "Your hosting provider" +msgstr "El teu proveïdor d'allotjament" + +#: src/view/screens/Settings.tsx:402 +#: src/view/shell/desktop/RightNav.tsx:137 +#: src/view/shell/Drawer.tsx:655 +msgid "Your invite codes are hidden when logged in using an App Password" +msgstr "Els teus codis d'invitació no es mostren quan has iniciat sessió amb una contrasenya d'aplicació" + +#: src/view/com/auth/onboarding/WelcomeMobile.tsx:59 +msgid "Your posts, likes, and blocks are public. Mutes are private." +msgstr "Les teves publicacions, m'agrades i bloquejos són públics. Els comptes silenciats són privats." + +#: src/view/com/modals/SwitchAccount.tsx:82 +msgid "Your profile" +msgstr "El teu perfil" + +#: src/view/com/auth/create/Step3.tsx:28 +msgid "Your user handle" +msgstr "El teu identificador d'usuari" + +#~ msgid "Appeal Decision" +#~ msgstr "Decisión de apelación" +#~ msgid "Looks like this feed is only available to users with a Bluesky account. Please sign up or sign in to view this feed!" +#~ msgstr "Parece que este canal de noticias sólo está disponible para usuarios con una cuenta Bluesky. Por favor, ¡regístrate o inicia sesión para ver este canal!" +#~ msgid "Please tell us why you think this decision was incorrect." +#~ msgstr "Por favor, dinos por qué crees que esta decisión fue incorrecta." +#~ msgid "This {0} has been labeled." +#~ msgstr "Este {0} ha sido etiquetado." +#~ msgid "What's next?" +#~ msgstr "¿Qué sigue?" + diff --git a/src/locale/locales/de/messages.po b/src/locale/locales/de/messages.po index 8c8ed45788..e9dce63bc5 100644 --- a/src/locale/locales/de/messages.po +++ b/src/locale/locales/de/messages.po @@ -345,11 +345,6 @@ msgstr "" msgid "Artistic or non-erotic nudity." msgstr "Künstlerische oder nicht-erotische Nacktheit." -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -364,6 +359,11 @@ msgstr "" msgid "Back" msgstr "Zurück" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "Grundlagen" @@ -398,6 +398,7 @@ msgstr "Diese Kontos sperren?" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -500,15 +501,6 @@ msgstr "Kamera" 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 "Darf nur Buchstaben, Zahlen, Leerzeichen, Bindestriche und Unterstriche enthalten. Muss mindestens 4 Zeichen lang sein, darf aber nicht länger als 32 Zeichen sein." -#: src/view/com/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -527,8 +519,17 @@ msgstr "" msgid "Cancel" msgstr "Abbrechen" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "Kontolöschung abbrechen" @@ -704,12 +705,6 @@ msgstr "" msgid "Compose reply" msgstr "Antwort zusammenstellen" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -719,6 +714,12 @@ msgstr "" msgid "Confirm" msgstr "Bestätigen" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -728,7 +729,7 @@ msgstr "Änderung bestätigen" msgid "Confirm content language settings" msgstr "Bestätigen Sie die Spracheinstellungen für den Inhalt" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "Bestätigen Sie Konto löschen" @@ -920,7 +921,7 @@ msgstr "App-Passwort löschen" msgid "Delete List" msgstr "Liste löschen" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "Mein Konto löschen" @@ -1624,7 +1625,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2014,6 +2015,10 @@ msgstr "" msgid "Mute thread" msgstr "Thema stummschalten" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "Stumme Kontos" @@ -2124,11 +2129,6 @@ msgstr "" msgid "Newest replies first" msgstr "" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2139,6 +2139,11 @@ msgstr "" msgid "Next" msgstr "Nächster" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "Nächstes Bild " @@ -2395,7 +2400,7 @@ msgstr "Seite nicht gefunden" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "Passwort" @@ -2473,7 +2478,7 @@ msgstr "Bitte geben Sie einen eindeutigen Namen für dieses App-Passwort ein ode msgid "Please enter your email." msgstr "Bitte geben Sie Ihre E-Mail-Adresse ein." -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "Bitte geben Sie auch Ihr Passwort ein:" @@ -3315,8 +3320,8 @@ msgid "Subscribe to this list" msgstr "Abonnieren Sie diese Liste" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3450,7 +3455,7 @@ msgstr "" msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -3628,16 +3633,16 @@ msgstr "Stummschaltliste aufheben" msgid "Unable to contact your service. Please check your Internet connection." msgstr "Sie können Ihren Dienst nicht kontaktieren. Bitte überprüfen Sie Ihre Internetverbindung." -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" -msgid "Unblock" -msgstr "" - #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "Freischalten" +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" +msgid "Unblock" +msgstr "" + #: src/view/com/profile/ProfileHeader.tsx:308 #: src/view/com/profile/ProfileHeader.tsx:392 msgid "Unblock Account" @@ -3847,6 +3852,10 @@ msgstr "Seite besuchen" msgid "Warn" msgstr "" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "" diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index c53a2c67e6..cb967bcaaa 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -373,11 +373,6 @@ msgstr "" #~ msgid "Ask apps to limit the visibility of my account" #~ msgstr "" -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -392,6 +387,11 @@ msgstr "" msgid "Back" msgstr "" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "" @@ -426,6 +426,7 @@ msgstr "" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -528,15 +529,6 @@ msgstr "" 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/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -555,8 +547,17 @@ msgstr "" msgid "Cancel" msgstr "" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "" @@ -736,12 +737,6 @@ msgstr "" msgid "Compose reply" msgstr "" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -751,6 +746,12 @@ msgstr "" msgid "Confirm" msgstr "" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -760,7 +761,7 @@ msgstr "" msgid "Confirm content language settings" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "" @@ -952,7 +953,7 @@ msgstr "" msgid "Delete List" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "" @@ -1673,7 +1674,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2075,6 +2076,10 @@ msgstr "" msgid "Mute thread" msgstr "" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "" @@ -2189,11 +2194,6 @@ msgstr "" msgid "Newest replies first" msgstr "" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2204,6 +2204,11 @@ msgstr "" msgid "Next" msgstr "" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "" @@ -2477,7 +2482,7 @@ msgstr "" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "" @@ -2555,7 +2560,7 @@ msgstr "" msgid "Please enter your email." msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "" @@ -3422,8 +3427,8 @@ msgid "Subscribe to this list" msgstr "" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3557,7 +3562,7 @@ msgstr "" msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -3735,13 +3740,13 @@ msgstr "" msgid "Unable to contact your service. Please check your Internet connection." msgstr "" -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" +#: src/view/com/profile/ProfileHeader.tsx:472 +#: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "" -#: src/view/com/profile/ProfileHeader.tsx:472 -#: src/view/screens/ProfileList.tsx:568 +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" msgid "Unblock" msgstr "" @@ -3958,6 +3963,10 @@ msgstr "" msgid "Warn" msgstr "" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "" diff --git a/src/locale/locales/es/messages.po b/src/locale/locales/es/messages.po index 25616816bf..7f4d5b73e4 100644 --- a/src/locale/locales/es/messages.po +++ b/src/locale/locales/es/messages.po @@ -341,11 +341,6 @@ msgstr "" msgid "Artistic or non-erotic nudity." msgstr "Desnudez artística o no erótica." -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -360,6 +355,11 @@ msgstr "" msgid "Back" msgstr "Regresar" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "Conceptos básicos" @@ -394,6 +394,7 @@ msgstr "¿Bloquear estas cuentas?" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -496,15 +497,6 @@ msgstr "Cámara" 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 "Sólo puede contener letras, números, espacios, guiones y guiones bajos. Debe tener al menos 4 caracteres, pero no más de 32." -#: src/view/com/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -523,8 +515,17 @@ msgstr "" msgid "Cancel" msgstr "Cancelar" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "Cancelar la eliminación de la cuenta" @@ -700,12 +701,6 @@ msgstr "" msgid "Compose reply" msgstr "Redactar la respuesta" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -715,6 +710,12 @@ msgstr "" msgid "Confirm" msgstr "Confirmar" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -724,7 +725,7 @@ msgstr "Confirmar el cambio" msgid "Confirm content language settings" msgstr "Confirmar la configuración del idioma del contenido" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "Confirmar eliminación de cuenta" @@ -916,7 +917,7 @@ msgstr "Borrar la contraseña de la app" msgid "Delete List" msgstr "Borrar la lista" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "Borrar mi cuenta" @@ -1620,7 +1621,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2006,6 +2007,10 @@ msgstr "" msgid "Mute thread" msgstr "Silenciar el hilo" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "Cuentas silenciadas" @@ -2116,11 +2121,6 @@ msgstr "" msgid "Newest replies first" msgstr "" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2131,6 +2131,11 @@ msgstr "" msgid "Next" msgstr "Siguiente" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "Imagen nueva" @@ -2387,7 +2392,7 @@ msgstr "Página no encontrada" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "Contraseña" @@ -2465,7 +2470,7 @@ msgstr "Introduce un nombre único para la contraseña de esta app o utiliza una msgid "Please enter your email." msgstr "Introduce tu correo electrónico." -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "Introduce tu contraseña, también:" @@ -3302,8 +3307,8 @@ msgid "Subscribe to this list" msgstr "Suscribirse a esta lista" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3437,7 +3442,7 @@ msgstr "" msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -3611,16 +3616,16 @@ msgstr "Desactivar la opción de silenciar la lista" msgid "Unable to contact your service. Please check your Internet connection." msgstr "No se puede contactar con tu servicio. Comprueba tu conexión a Internet." -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" -msgid "Unblock" -msgstr "" - #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "Desbloquear" +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" +msgid "Unblock" +msgstr "" + #: src/view/com/profile/ProfileHeader.tsx:308 #: src/view/com/profile/ProfileHeader.tsx:392 msgid "Unblock Account" @@ -3830,6 +3835,10 @@ msgstr "Visitar el sitio" msgid "Warn" msgstr "" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "" diff --git a/src/locale/locales/fr/messages.po b/src/locale/locales/fr/messages.po index db3a584b67..305336c0ec 100644 --- a/src/locale/locales/fr/messages.po +++ b/src/locale/locales/fr/messages.po @@ -9,42 +9,38 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "PO-Revision-Date: \n" -"Last-Translator: \n" +"Last-Translator: Stanislas Signoud (@signez.fr)\n" "Language-Team: \n" "Plural-Forms: \n" #: src/view/com/modals/VerifyEmail.tsx:142 msgid "(no email)" -msgstr "" +msgstr "(pas d’e-mail)" #: src/view/shell/desktop/RightNav.tsx:168 msgid "{0, plural, one {# invite code available} other {# invite codes available}}" -msgstr "{0, plural, one {# invite code available} other {# invite codes available}}" +msgstr "{0, plural, one {# code d’invitation disponible} other {# codes d’invitations disponibles}}" #: src/view/com/modals/CreateOrEditList.tsx:185 #: src/view/screens/Settings.tsx:294 msgid "{0}" msgstr "{0}" -#: src/view/com/modals/CreateOrEditList.tsx:176 -#~ msgid "{0} {purposeLabel} List" -#~ msgstr "{0} {purposeLabel} Liste" - #: src/view/com/profile/ProfileHeader.tsx:632 msgid "{following} following" -msgstr "" +msgstr "{following} abonnements" #: src/view/shell/desktop/RightNav.tsx:151 msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}" -msgstr "{invitesAvailable, plural, one {Invite codes: # available} other {Invite codes: # available}}" +msgstr "{invitesAvailable, plural, one {Code d’invitation : # disponible} other {Codes d’invitation : # disponibles}}" #: src/view/screens/Settings.tsx:435 -#: src/view/shell/Drawer.tsx:661 +#: src/view/shell/Drawer.tsx:664 msgid "{invitesAvailable} invite code available" msgstr "{invitesAvailable} code d’invitation disponible" #: src/view/screens/Settings.tsx:437 -#: src/view/shell/Drawer.tsx:663 +#: src/view/shell/Drawer.tsx:666 msgid "{invitesAvailable} invite codes available" msgstr "{invitesAvailable} codes d’invitation disponibles" @@ -52,13 +48,13 @@ msgstr "{invitesAvailable} codes d’invitation disponibles" msgid "{message}" msgstr "{message}" -#: src/view/shell/Drawer.tsx:440 +#: src/view/shell/Drawer.tsx:443 msgid "{numUnreadNotifications} unread" -msgstr "" +msgstr "{numUnreadNotifications} non lus" #: src/Navigation.tsx:147 msgid "@{0}" -msgstr "" +msgstr "@{0}" #: src/view/com/threadgate/WhoCanReply.tsx:158 msgid "<0/> members" @@ -66,7 +62,7 @@ msgstr "<0/> membres" #: src/view/com/profile/ProfileHeader.tsx:634 msgid "<0>{following} <1>following" -msgstr "" +msgstr "<0>{following} <1>abonnements" #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:30 msgid "<0>Choose your<1>Recommended<2>Feeds" @@ -74,15 +70,15 @@ msgstr "<0>Choisissez vos<1>fils d’actualité<2>recommandés" #: src/view/com/auth/onboarding/RecommendedFollows.tsx:37 msgid "<0>Follow some<1>Recommended<2>Users" -msgstr "<0>Suivre certains<1>utilisateurs<2>recommandés" +msgstr "<0>Suivre certains<1>comptes<2>recommandés" #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:21 msgid "<0>Welcome to<1>Bluesky" -msgstr "" +msgstr "<0>Bienvenue sur<1>Bluesky" #: src/view/com/profile/ProfileHeader.tsx:597 msgid "⚠Invalid Handle" -msgstr "" +msgstr "⚠Pseudo invalide" #: src/view/com/util/moderation/LabelInfo.tsx:45 msgid "A content warning has been applied to this {0}." @@ -95,11 +91,11 @@ msgstr "Une nouvelle version de l’application est disponible. Veuillez faire l #: src/view/com/util/ViewHeader.tsx:83 #: src/view/screens/Search/Search.tsx:538 msgid "Access navigation links and settings" -msgstr "" +msgstr "Accède aux liens de navigation et aux paramètres" -#: src/view/com/pager/FeedsTabBarMobile.tsx:78 +#: src/view/com/pager/FeedsTabBarMobile.tsx:83 msgid "Access profile and other navigation links" -msgstr "" +msgstr "Accède au profil et aux autres liens de navigation" #: src/view/com/modals/EditImage.tsx:299 #: src/view/screens/Settings.tsx:445 @@ -113,19 +109,19 @@ msgstr "Compte" #: src/view/com/profile/ProfileHeader.tsx:293 msgid "Account blocked" -msgstr "" +msgstr "Compte bloqué" #: src/view/com/profile/ProfileHeader.tsx:260 msgid "Account muted" -msgstr "" +msgstr "Compte masqué" #: src/view/com/modals/ModerationDetails.tsx:86 msgid "Account Muted" -msgstr "" +msgstr "Compte masqué" #: src/view/com/modals/ModerationDetails.tsx:72 msgid "Account Muted by List" -msgstr "" +msgstr "Compte masqué par liste" #: src/view/com/util/AccountDropdownBtn.tsx:41 msgid "Account options" @@ -133,15 +129,15 @@ msgstr "Options de compte" #: src/view/com/util/AccountDropdownBtn.tsx:25 msgid "Account removed from quick access" -msgstr "" +msgstr "Compte supprimé de l’accès rapide" #: src/view/com/profile/ProfileHeader.tsx:315 msgid "Account unblocked" -msgstr "" +msgstr "Compte débloqué" #: src/view/com/profile/ProfileHeader.tsx:273 msgid "Account unmuted" -msgstr "" +msgstr "Compte démasqué" #: src/view/com/auth/onboarding/RecommendedFeedsItem.tsx:150 #: src/view/com/modals/ListAddRemoveUsers.tsx:264 @@ -156,7 +152,7 @@ msgstr "Ajouter un avertissement sur le contenu" #: src/view/screens/ProfileList.tsx:781 msgid "Add a user to this list" -msgstr "Ajouter un utilisateur à cette liste" +msgstr "Ajouter un compte à cette liste" #: src/view/screens/Settings.tsx:383 #: src/view/screens/Settings.tsx:392 @@ -173,7 +169,7 @@ msgstr "Ajouter un texte alt" #: src/view/screens/AppPasswords.tsx:143 #: src/view/screens/AppPasswords.tsx:156 msgid "Add App Password" -msgstr "" +msgstr "Ajouter un mot de passe d’application" #: src/view/com/modals/report/InputIssueDetails.tsx:41 #: src/view/com/modals/report/Modal.tsx:191 @@ -190,11 +186,11 @@ msgstr "Ajouter une carte de lien" #: src/view/com/composer/Composer.tsx:451 msgid "Add link card:" -msgstr "Ajouter une carte de lien :" +msgstr "Ajouter une carte de lien :" #: src/view/com/modals/ChangeHandle.tsx:417 msgid "Add the following DNS record to your domain:" -msgstr "Ajoutez l’enregistrement DNS suivant à votre domaine :" +msgstr "Ajoutez l’enregistrement DNS suivant à votre domaine :" #: src/view/com/profile/ProfileHeader.tsx:357 msgid "Add to Lists" @@ -207,7 +203,7 @@ msgstr "Ajouter à mes fils d’actu" #: src/view/com/auth/onboarding/RecommendedFeedsItem.tsx:139 msgid "Added" -msgstr "" +msgstr "Ajouté" #: src/view/com/modals/ListAddRemoveUsers.tsx:191 #: src/view/com/modals/UserAddRemoveLists.tsx:128 @@ -216,7 +212,7 @@ msgstr "Ajouté à la liste" #: src/view/com/feeds/FeedSourceCard.tsx:125 msgid "Added to my feeds" -msgstr "" +msgstr "Ajouté à mes fils d’actu" #: src/view/screens/PreferencesHomeFeed.tsx:173 msgid "Adjust the number of likes a reply must have to be shown in your feed." @@ -228,7 +224,7 @@ msgstr "Contenu pour adultes" #: src/view/com/modals/ContentFilteringSettings.tsx:137 msgid "Adult content can only be enabled via the Web at <0/>." -msgstr "" +msgstr "Le contenu pour adultes ne peut être activé que via le Web à <0/>." #: src/view/screens/Settings.tsx:630 msgid "Advanced" @@ -236,7 +232,7 @@ msgstr "Avancé" #: src/view/com/auth/login/ChooseAccountForm.tsx:98 msgid "Already signed in as @{0}" -msgstr "" +msgstr "Déjà connecté·e en tant que @{0}" #: src/view/com/composer/photos/Gallery.tsx:130 msgid "ALT" @@ -248,7 +244,7 @@ msgstr "Texte Alt" #: src/view/com/composer/photos/Gallery.tsx:209 msgid "Alt text describes images for blind and low-vision users, and helps give context to everyone." -msgstr "Le texte Alt décrit les images pour les utilisateurs aveugles et malvoyants, et aide à donner un contexte à tout le monde." +msgstr "Le texte Alt décrit les images pour les personnes aveugles et malvoyantes, et aide à donner un contexte à tout le monde." #: src/view/com/modals/VerifyEmail.tsx:124 msgid "An email has been sent to {0}. It includes a confirmation code which you can enter below." @@ -261,7 +257,7 @@ msgstr "Un courriel a été envoyé à votre ancienne adresse, {0}. Il comprend #: src/view/com/profile/FollowButton.tsx:30 #: src/view/com/profile/FollowButton.tsx:40 msgid "An issue occurred, please try again." -msgstr "" +msgstr "Un problème est survenu, veuillez réessayer." #: src/view/com/notifications/FeedItem.tsx:240 #: src/view/com/threadgate/WhoCanReply.tsx:178 @@ -274,19 +270,19 @@ msgstr "Langue de l’application" #: src/view/screens/AppPasswords.tsx:228 msgid "App password deleted" -msgstr "" +msgstr "Mot de passe d’application supprimé" #: src/view/com/modals/AddAppPasswords.tsx:133 msgid "App Password names can only contain letters, numbers, spaces, dashes, and underscores." -msgstr "" +msgstr "Les noms de mots de passe d’application ne peuvent contenir que des lettres, des chiffres, des espaces, des tirets et des tirets bas." #: src/view/com/modals/AddAppPasswords.tsx:98 msgid "App Password names must be at least 4 characters long." -msgstr "" +msgstr "Les noms de mots de passe d’application doivent comporter au moins 4 caractères." #: src/view/screens/Settings.tsx:641 msgid "App password settings" -msgstr "" +msgstr "Paramètres de mot de passe d’application" #: src/view/screens/Settings.tsx:650 msgid "App passwords" @@ -305,9 +301,6 @@ msgstr "Faire appel de l’avertissement sur le contenu" msgid "Appeal Content Warning" msgstr "Appel de l’avertissement sur le contenu" -#~ msgid "Appeal Decision" -#~ msgstr "Appel de la décision" - #: src/view/com/util/moderation/LabelInfo.tsx:52 msgid "Appeal this decision" msgstr "Faire appel de cette décision" @@ -322,19 +315,19 @@ msgstr "Affichage" #: src/view/screens/AppPasswords.tsx:224 msgid "Are you sure you want to delete the app password \"{name}\"?" -msgstr "Êtes-vous sûr de vouloir supprimer le mot de passe de l’application « {name} » ?" +msgstr "Êtes-vous sûr de vouloir supprimer le mot de passe de l’application « {name} » ?" #: src/view/com/composer/Composer.tsx:143 msgid "Are you sure you'd like to discard this draft?" -msgstr "Êtes-vous sûr de vouloir rejeter ce brouillon ?" +msgstr "Êtes-vous sûr de vouloir rejeter ce brouillon ?" #: src/view/screens/ProfileList.tsx:364 msgid "Are you sure?" -msgstr "Vous confirmez ?" +msgstr "Vous confirmez ?" #: src/view/com/util/forms/PostDropdownBtn.tsx:231 msgid "Are you sure? This cannot be undone." -msgstr "Vous confirmez ? Cela ne pourra pas être annulé." +msgstr "Vous confirmez ? Cela ne pourra pas être annulé." #: src/view/com/composer/select-language/SuggestedLanguage.tsx:65 msgid "Are you writing in <0>{0}?" @@ -347,9 +340,9 @@ msgstr "Nudité artistique ou non érotique." #: src/view/com/post-thread/PostThread.tsx:400 msgctxt "action" msgid "Back" -msgstr "" +msgstr "Retour" -#: src/view/com/auth/create/CreateAccount.tsx:141 +#: src/view/com/auth/create/CreateAccount.tsx:142 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 #: src/view/com/auth/login/LoginForm.tsx:256 @@ -367,14 +360,14 @@ msgstr "Arrière" msgid "Basics" msgstr "Principes de base" -#: src/view/com/auth/create/Step2.tsx:156 +#: src/view/com/auth/create/Step1.tsx:194 #: src/view/com/modals/BirthDateSettings.tsx:73 msgid "Birthday" msgstr "Date de naissance" #: src/view/screens/Settings.tsx:340 msgid "Birthday:" -msgstr "Date de naissance :" +msgstr "Date de naissance :" #: src/view/com/profile/ProfileHeader.tsx:286 #: src/view/com/profile/ProfileHeader.tsx:393 @@ -391,15 +384,16 @@ msgstr "Liste de blocage" #: src/view/screens/ProfileList.tsx:315 msgid "Block these accounts?" -msgstr "Bloquer ces comptes ?" +msgstr "Bloquer ces comptes ?" #: src/view/screens/ProfileList.tsx:319 msgid "Block this List" -msgstr "" +msgstr "Bloquer cette liste" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" -msgstr "" +msgstr "Bloqué" #: src/view/screens/Moderation.tsx:123 msgid "Blocked accounts" @@ -455,7 +449,7 @@ msgstr "Bluesky distribue des invitations pour construire une communauté plus s #: src/view/screens/Moderation.tsx:225 msgid "Bluesky will not show your profile and posts to logged-out users. Other apps may not honor this request. This does not make your account private." -msgstr "Bluesky n’affichera pas votre profil et vos messages à des utilisateurs non connectés. Il est possible que d’autres applications n’honorent pas cette demande. Cela ne privatise pas votre compte." +msgstr "Bluesky n’affichera pas votre profil et vos messages à des personnes non connectées. Il est possible que d’autres applications n’honorent pas cette demande. Cela ne privatise pas votre compte." #: src/view/com/modals/ServerInput.tsx:78 msgid "Bluesky.Social" @@ -471,23 +465,23 @@ msgstr "Affaires" #: src/view/com/modals/ServerInput.tsx:115 msgid "Button disabled. Input custom domain to proceed." -msgstr "" +msgstr "Bouton désactivé. Saisissez un domaine personnalisé pour continuer." #: src/view/com/profile/ProfileSubpageHeader.tsx:157 msgid "by —" -msgstr "" +msgstr "par —" #: src/view/com/auth/onboarding/RecommendedFeedsItem.tsx:100 msgid "by {0}" -msgstr "" +msgstr "par {0}" #: src/view/com/profile/ProfileSubpageHeader.tsx:161 msgid "by <0/>" -msgstr "" +msgstr "par <0/>" #: src/view/com/profile/ProfileSubpageHeader.tsx:159 msgid "by you" -msgstr "" +msgstr "par vous" #: src/view/com/composer/photos/OpenCameraBtn.tsx:60 #: src/view/com/util/UserAvatar.tsx:221 @@ -503,11 +497,12 @@ msgstr "Ne peut contenir que des lettres, des chiffres, des espaces, des tirets #: src/view/com/modals/Confirm.tsx:91 #: src/view/com/modals/CreateOrEditList.tsx:293 #: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 +#: src/view/com/modals/DeleteAccount.tsx:230 msgctxt "action" msgid "Cancel" -msgstr "" +msgstr "Annuler" +#: src/components/Prompt.tsx:92 #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -527,14 +522,10 @@ msgid "Cancel" msgstr "Annuler" #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "Annuler la suppression de compte" -#: src/view/com/modals/AltImage.tsx:123 -#~ msgid "Cancel add image alt text" -#~ msgstr "Annuler l’ajout d’un texte alt à l’image" - #: src/view/com/modals/ChangeHandle.tsx:149 msgid "Cancel change handle" msgstr "Annuler le changement de pseudo" @@ -563,20 +554,16 @@ msgstr "Annuler l’inscription sur la liste d’attente" #: src/view/screens/Settings.tsx:334 msgctxt "action" msgid "Change" -msgstr "" - -#: src/view/screens/Settings.tsx:306 -#~ msgid "Change" -#~ msgstr "Changer" +msgstr "Modifier" #: src/view/screens/Settings.tsx:662 #: src/view/screens/Settings.tsx:671 msgid "Change handle" -msgstr "Changer le pseudo" +msgstr "Modifier le pseudo" #: src/view/com/modals/ChangeHandle.tsx:161 msgid "Change Handle" -msgstr "Changer le pseudo" +msgstr "Modifier le pseudo" #: src/view/com/modals/VerifyEmail.tsx:147 msgid "Change my email" @@ -588,7 +575,7 @@ msgstr "" #: src/view/com/modals/ChangeEmail.tsx:109 msgid "Change Your Email" -msgstr "Modifiez votre e-mail" +msgstr "Modifier votre e-mail" #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:121 msgid "Check out some recommended feeds. Tap + to add them to your list of pinned feeds." @@ -596,19 +583,19 @@ msgstr "Consultez quelques fils d’actu recommandés. Appuyez sur + pour les aj #: src/view/com/auth/onboarding/RecommendedFollows.tsx:185 msgid "Check out some recommended users. Follow them to see similar users." -msgstr "Consultez quelques utilisateurs recommandés. Suivez-les pour voir des utilisateurs similaires." +msgstr "Consultez quelques comptes recommandés. Suivez-les pour voir des personnes similaires." #: src/view/com/modals/DeleteAccount.tsx:165 msgid "Check your inbox for an email with the confirmation code to enter below:" -msgstr "Consultez votre boîte de réception, vous avez du recevoir un e-mail contenant un code de confirmation à saisir ci-dessous :" +msgstr "Consultez votre boîte de réception, vous avez du recevoir un e-mail contenant un code de confirmation à saisir ci-dessous :" #: src/view/com/modals/Threadgate.tsx:72 msgid "Choose \"Everybody\" or \"Nobody\"" -msgstr "Choisir « Tout le monde » ou « Personne »" +msgstr "Choisir « Tout le monde » ou « Personne »" #: src/view/screens/Settings.tsx:663 msgid "Choose a new Bluesky username or create" -msgstr "" +msgstr "Choisir un nouveau pseudo Bluesky ou en créer un" #: src/view/com/modals/ServerInput.tsx:38 msgid "Choose Service" @@ -619,7 +606,7 @@ msgstr "Choisir un service" msgid "Choose the algorithms that power your experience with custom feeds." msgstr "Choisissez les algorithmes qui alimentent votre expérience avec des fils d’actualité personnalisés." -#: src/view/com/auth/create/Step2.tsx:127 +#: src/view/com/auth/create/Step1.tsx:163 msgid "Choose your password" msgstr "Choisissez votre mot de passe" @@ -648,6 +635,10 @@ msgstr "Effacer la recherche" #: src/view/screens/Support.tsx:40 msgid "click here" +msgstr "cliquez ici" + +#: src/components/Dialog/index.web.tsx:78 +msgid "Close active dialog" msgstr "" #: src/view/com/auth/login/PasswordUpdatedForm.tsx:38 @@ -666,29 +657,29 @@ msgstr "Fermer l’image" msgid "Close image viewer" msgstr "Fermer la visionneuse d’images" -#: src/view/shell/index.web.tsx:49 +#: src/view/shell/index.web.tsx:51 msgid "Close navigation footer" msgstr "Fermer le pied de page de navigation" -#: src/view/shell/index.web.tsx:50 +#: src/view/shell/index.web.tsx:52 msgid "Closes bottom navigation bar" -msgstr "" +msgstr "Ferme la barre de navigation du bas" #: src/view/com/auth/login/PasswordUpdatedForm.tsx:39 msgid "Closes password update alert" -msgstr "" +msgstr "Ferme la notification de mise à jour du mot de passe" #: src/view/com/composer/Composer.tsx:302 msgid "Closes post composer and discards post draft" -msgstr "" +msgstr "Ferme la fenêtre de rédaction et supprime le brouillon" #: src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx:27 msgid "Closes viewer for header image" -msgstr "" +msgstr "Ferme la visionneuse pour l’image d’en-tête" #: src/view/com/notifications/FeedItem.tsx:321 msgid "Collapses list of users for a given notification" -msgstr "" +msgstr "Réduit la liste des comptes pour une notification donnée" #: src/Navigation.tsx:229 #: src/view/screens/CommunityGuidelines.tsx:32 @@ -697,7 +688,7 @@ msgstr "Directives communautaires" #: src/view/com/composer/Composer.tsx:417 msgid "Compose posts up to {MAX_GRAPHEME_LENGTH} characters in length" -msgstr "" +msgstr "Permet d’écrire des messages de {MAX_GRAPHEME_LENGTH} caractères maximum" #: src/view/com/composer/Prompt.tsx:24 msgid "Compose reply" @@ -707,8 +698,9 @@ msgstr "Rédiger une réponse" #: src/view/com/modals/Confirm.tsx:78 msgctxt "action" msgid "Confirm" -msgstr "" +msgstr "Confirmer" +#: src/components/Prompt.tsx:114 #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -727,13 +719,13 @@ msgstr "Confirmer le changement" msgid "Confirm content language settings" msgstr "Confirmer les paramètres de langue" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "Confirmer la suppression du compte" #: src/view/com/modals/ContentFilteringSettings.tsx:151 msgid "Confirm your age to enable adult content." -msgstr "" +msgstr "Confirmez votre âge pour activer le contenu pour adultes." #: src/view/com/modals/ChangeEmail.tsx:157 #: src/view/com/modals/DeleteAccount.tsx:178 @@ -743,13 +735,17 @@ msgstr "Code de confirmation" #: src/view/com/modals/Waitlist.tsx:120 msgid "Confirms signing up {email} to the waitlist" -msgstr "" +msgstr "Confirme l’inscription de {email} sur la liste d’attente" -#: src/view/com/auth/create/CreateAccount.tsx:174 +#: src/view/com/auth/create/CreateAccount.tsx:175 #: src/view/com/auth/login/LoginForm.tsx:275 msgid "Connecting..." msgstr "Connexion…" +#: src/view/com/auth/create/CreateAccount.tsx:195 +msgid "Contact support" +msgstr "" + #: src/view/screens/Moderation.tsx:81 msgid "Content filtering" msgstr "Filtrage du contenu" @@ -765,7 +761,7 @@ msgstr "Langues du contenu" #: src/view/com/modals/ModerationDetails.tsx:65 msgid "Content Not Available" -msgstr "" +msgstr "Contenu non disponible" #: src/view/com/modals/ModerationDetails.tsx:33 #: src/view/com/util/moderation/ScreenHider.tsx:78 @@ -788,17 +784,17 @@ msgstr "Copié" #: src/view/screens/Settings.tsx:243 msgid "Copied build version to clipboard" -msgstr "" +msgstr "Version de build copiée dans le presse-papier" #: src/view/com/modals/AddAppPasswords.tsx:75 #: src/view/com/modals/InviteCodes.tsx:152 #: src/view/com/util/forms/PostDropdownBtn.tsx:110 msgid "Copied to clipboard" -msgstr "" +msgstr "Copié dans le presse-papier" #: src/view/com/modals/AddAppPasswords.tsx:191 msgid "Copies app password" -msgstr "" +msgstr "Copie le mot de passe d’application" #: src/view/com/modals/AddAppPasswords.tsx:190 msgid "Copy" @@ -806,7 +802,7 @@ msgstr "Copie" #: src/view/screens/ProfileList.tsx:396 msgid "Copy link to list" -msgstr "Copier le lien dans la liste" +msgstr "Copier le lien vers la liste" #: src/view/com/util/forms/PostDropdownBtn.tsx:151 msgid "Copy link to post" @@ -814,7 +810,7 @@ msgstr "Copier le lien vers le post" #: src/view/com/profile/ProfileHeader.tsx:342 msgid "Copy link to profile" -msgstr "Copier le lien sur le profil" +msgstr "Copier le lien vers le profil" #: src/view/com/util/forms/PostDropdownBtn.tsx:137 msgid "Copy post text" @@ -841,15 +837,15 @@ msgstr "Créer un nouveau compte" #: src/view/screens/Settings.tsx:384 msgid "Create a new Bluesky account" -msgstr "" +msgstr "Créer un compte Bluesky" -#: src/view/com/auth/create/CreateAccount.tsx:121 +#: src/view/com/auth/create/CreateAccount.tsx:122 msgid "Create Account" msgstr "Créer un compte" #: src/view/com/modals/AddAppPasswords.tsx:228 msgid "Create App Password" -msgstr "" +msgstr "Créer un mot de passe d’application" #: src/view/com/auth/HomeLoggedOutCTA.tsx:54 #: src/view/com/auth/SplashScreen.tsx:43 @@ -862,15 +858,15 @@ msgstr "{0} créé" #: src/view/screens/ProfileFeed.tsx:625 msgid "Created by <0/>" -msgstr "" +msgstr "Créée par <0/>" #: src/view/screens/ProfileFeed.tsx:623 msgid "Created by you" -msgstr "" +msgstr "Créée par vous" #: src/view/com/composer/Composer.tsx:448 msgid "Creates a card with a thumbnail. The card links to {url}" -msgstr "" +msgstr "Crée une carte avec une miniature. La carte pointe vers {url}" #: src/view/com/modals/ChangeHandle.tsx:389 #: src/view/com/modals/ServerInput.tsx:102 @@ -879,7 +875,7 @@ msgstr "Domaine personnalisé" #: src/view/screens/PreferencesExternalEmbeds.tsx:55 msgid "Customize media from external sites." -msgstr "" +msgstr "Personnaliser les médias provenant de sites externes." #: src/view/screens/Settings.tsx:687 msgid "Danger Zone" @@ -887,19 +883,19 @@ msgstr "Zone de danger" #: src/view/screens/Settings.tsx:479 msgid "Dark" -msgstr "" +msgstr "Sombre" #: src/view/screens/Debug.tsx:63 msgid "Dark mode" -msgstr "" +msgstr "Mode sombre" #: src/Navigation.tsx:204 -msgid "Debug" -msgstr "" +#~ msgid "Debug" +#~ msgstr "Débug" #: src/view/screens/Debug.tsx:83 msgid "Debug panel" -msgstr "" +msgstr "Panneau de débug" #: src/view/screens/Settings.tsx:694 msgid "Delete account" @@ -919,7 +915,7 @@ msgstr "Supprimer le mot de passe de l’appli" msgid "Delete List" msgstr "Supprimer la liste" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "Supprimer mon compte" @@ -933,11 +929,11 @@ msgstr "Supprimer le post" #: src/view/com/util/forms/PostDropdownBtn.tsx:230 msgid "Delete this post?" -msgstr "Supprimer ce post ?" +msgstr "Supprimer ce post ?" #: src/view/com/util/post-embeds/QuoteEmbed.tsx:66 msgid "Deleted" -msgstr "" +msgstr "Supprimé" #: src/view/com/post-thread/PostThread.tsx:246 msgid "Deleted post." @@ -951,8 +947,8 @@ msgid "Description" msgstr "Description" #: src/view/com/auth/create/Step1.tsx:96 -msgid "Dev Server" -msgstr "Serveur de dév" +#~ msgid "Dev Server" +#~ msgstr "Serveur de dév" #: src/view/screens/Settings.tsx:711 msgid "Developer Tools" @@ -960,7 +956,7 @@ msgstr "Outils de dév" #: src/view/com/composer/Composer.tsx:211 msgid "Did you want to say anything?" -msgstr "Vous vouliez dire quelque chose ?" +msgstr "Vous vouliez dire quelque chose ?" #: src/view/com/composer/Composer.tsx:144 msgid "Discard" @@ -972,12 +968,12 @@ msgstr "Ignorer le brouillon" #: src/view/screens/Moderation.tsx:207 msgid "Discourage apps from showing my account to logged-out users" -msgstr "Empêcher les applis de montrer mon compte aux utilisateurs déconnectés" +msgstr "Empêcher les applis de montrer mon compte aux personnes non connectées" #: src/view/com/posts/FollowingEmptyState.tsx:74 #: src/view/com/posts/FollowingEndOfFeed.tsx:75 msgid "Discover new custom feeds" -msgstr "" +msgstr "Découvrir des fils d’actu personnalisés" #: src/view/screens/Feeds.tsx:409 msgid "Discover new feeds" @@ -993,11 +989,11 @@ msgstr "Afficher le nom" #: src/view/com/modals/ChangeHandle.tsx:487 msgid "Domain verified!" -msgstr "Domaine vérifié !" +msgstr "Domaine vérifié !" -#: src/view/com/auth/create/Step2.tsx:83 +#: src/view/com/auth/create/Step1.tsx:114 msgid "Don't have an invite code?" -msgstr "" +msgstr "Pas de code d’invitation ?" #: src/view/com/auth/onboarding/RecommendedFollows.tsx:86 #: src/view/com/modals/EditImage.tsx:333 @@ -1010,7 +1006,7 @@ msgstr "" #: src/view/screens/PreferencesThreads.tsx:162 msgctxt "action" msgid "Done" -msgstr "" +msgstr "Terminer" #: src/view/com/modals/AddAppPasswords.tsx:228 #: src/view/com/modals/AltImage.tsx:115 @@ -1030,31 +1026,31 @@ msgstr "Terminé{extraText}" #: src/view/com/auth/login/ChooseAccountForm.tsx:45 msgid "Double tap to sign in" -msgstr "" +msgstr "Tapotez deux fois pour vous connecter" #: src/view/com/modals/EditProfile.tsx:185 msgid "e.g. Alice Roberts" -msgstr "" +msgstr "ex. Alice Dupont" #: src/view/com/modals/EditProfile.tsx:203 msgid "e.g. Artist, dog-lover, and avid reader." -msgstr "" +msgstr "ex. Artiste, amoureuse des chiens et lectrice passionnée." #: src/view/com/modals/CreateOrEditList.tsx:225 msgid "e.g. Great Posters" -msgstr "" +msgstr "ex. Les meilleurs comptes" #: src/view/com/modals/CreateOrEditList.tsx:226 msgid "e.g. Spammers" -msgstr "" +msgstr "ex. Spammeurs" #: src/view/com/modals/CreateOrEditList.tsx:246 msgid "e.g. The posters who never miss." -msgstr "" +msgstr "ex. Ces comptes qui ne ratent jamais leur coup." #: src/view/com/modals/CreateOrEditList.tsx:247 msgid "e.g. Users that repeatedly reply with ads." -msgstr "" +msgstr "ex. Les comptes qui répondent toujours avec des pubs." #: src/view/com/modals/InviteCodes.tsx:96 msgid "Each code works once. You'll receive more invite codes periodically." @@ -1063,7 +1059,7 @@ msgstr "Chaque code ne fonctionne qu’une seule fois. Vous recevrez régulière #: src/view/com/lists/ListMembers.tsx:149 msgctxt "action" msgid "Edit" -msgstr "" +msgstr "Modifier" #: src/view/com/composer/photos/Gallery.tsx:144 #: src/view/com/modals/EditImage.tsx:207 @@ -1076,7 +1072,7 @@ msgstr "Modifier les infos de la liste" #: src/view/com/modals/CreateOrEditList.tsx:193 msgid "Edit Moderation List" -msgstr "" +msgstr "Modifier la liste de modération" #: src/Navigation.tsx:244 #: src/view/screens/Feeds.tsx:371 @@ -1102,24 +1098,26 @@ msgstr "Modifier les fils d’actu enregistrés" #: src/view/com/modals/CreateOrEditList.tsx:188 msgid "Edit User List" -msgstr "" +msgstr "Modifier la liste de comptes" #: src/view/com/modals/EditProfile.tsx:193 msgid "Edit your display name" -msgstr "" +msgstr "Modifier votre nom d’affichage" #: src/view/com/modals/EditProfile.tsx:211 msgid "Edit your profile description" -msgstr "" +msgstr "Modifier votre description de profil" -#: src/view/com/auth/create/Step2.tsx:108 +#: src/view/com/auth/create/Step1.tsx:143 +#: src/view/com/auth/create/Step2.tsx:90 +#: src/view/com/auth/create/Step2.tsx:164 #: src/view/com/auth/login/ForgotPasswordForm.tsx:152 #: src/view/com/modals/ChangeEmail.tsx:141 #: src/view/com/modals/Waitlist.tsx:88 msgid "Email" msgstr "E-mail" -#: src/view/com/auth/create/Step2.tsx:99 +#: src/view/com/auth/create/Step1.tsx:134 #: src/view/com/auth/login/ForgotPasswordForm.tsx:143 msgid "Email address" msgstr "Adresse e-mail" @@ -1127,7 +1125,7 @@ msgstr "Adresse e-mail" #: src/view/com/modals/ChangeEmail.tsx:56 #: src/view/com/modals/ChangeEmail.tsx:88 msgid "Email updated" -msgstr "" +msgstr "Adresse e-mail mise à jour" #: src/view/com/modals/ChangeEmail.tsx:111 msgid "Email Updated" @@ -1135,27 +1133,27 @@ msgstr "E-mail mis à jour" #: src/view/com/modals/VerifyEmail.tsx:78 msgid "Email verified" -msgstr "" +msgstr "Adresse e-mail vérifiée" #: src/view/screens/Settings.tsx:312 msgid "Email:" -msgstr "E-mail :" +msgstr "E-mail :" #: src/view/com/modals/EmbedConsent.tsx:113 msgid "Enable {0} only" -msgstr "" +msgstr "Activer {0} uniquement" #: src/view/com/modals/ContentFilteringSettings.tsx:162 msgid "Enable Adult Content" -msgstr "" +msgstr "Activer le contenu pour adultes" #: src/view/com/modals/EmbedConsent.tsx:97 msgid "Enable External Media" -msgstr "" +msgstr "Activer les médias externes" #: src/view/screens/PreferencesExternalEmbeds.tsx:75 msgid "Enable media players for" -msgstr "" +msgstr "Activer les lecteurs médias pour" #: src/view/screens/PreferencesHomeFeed.tsx:147 msgid "Enable this setting to only see replies between people you follow." @@ -1167,15 +1165,15 @@ msgstr "Fin du fil d’actu" #: src/view/com/modals/AddAppPasswords.tsx:165 msgid "Enter a name for this App Password" -msgstr "" +msgstr "Entrer un nom pour ce mot de passe d’application" #: src/view/com/modals/VerifyEmail.tsx:105 msgid "Enter Confirmation Code" -msgstr "" +msgstr "Entrer un code de confirmation" #: src/view/com/auth/create/Step1.tsx:71 -msgid "Enter the address of your provider:" -msgstr "Saisissez l’adresse de votre fournisseur :" +#~ msgid "Enter the address of your provider:" +#~ msgstr "Saisissez l’adresse de votre hébergeur :" #: src/view/com/modals/ChangeHandle.tsx:371 msgid "Enter the domain you want to use" @@ -1183,36 +1181,40 @@ msgstr "Entrez le domaine que vous voulez utiliser" #: src/view/com/auth/login/ForgotPasswordForm.tsx:103 msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." -msgstr "Saisissez l’e-mail que vous avez utilisé pour créer votre compte. Nous vous enverrons un « code de réinitialisation » afin changer votre mot de passe." +msgstr "Saisissez l’e-mail que vous avez utilisé pour créer votre compte. Nous vous enverrons un « code de réinitialisation » afin changer votre mot de passe." -#: src/view/com/auth/create/Step2.tsx:157 +#: src/view/com/auth/create/Step1.tsx:195 #: src/view/com/modals/BirthDateSettings.tsx:74 msgid "Enter your birth date" -msgstr "" +msgstr "Saisissez votre date de naissance" #: src/view/com/modals/Waitlist.tsx:78 msgid "Enter your email" -msgstr "" +msgstr "Entrez votre e-mail" -#: src/view/com/auth/create/Step2.tsx:104 +#: src/view/com/auth/create/Step1.tsx:139 msgid "Enter your email address" msgstr "Entrez votre e-mail" #: src/view/com/modals/ChangeEmail.tsx:41 msgid "Enter your new email above" -msgstr "" +msgstr "Entrez votre nouvel e-mail ci-dessus" #: src/view/com/modals/ChangeEmail.tsx:117 msgid "Enter your new email address below." msgstr "Entrez votre nouvelle e-mail ci-dessous." +#: src/view/com/auth/create/Step2.tsx:84 +msgid "Enter your phone number" +msgstr "" + #: src/view/com/auth/login/Login.tsx:99 msgid "Enter your username and password" -msgstr "Entrez votre nom d’utilisateur et votre mot de passe" +msgstr "Entrez votre pseudo et votre mot de passe" #: src/view/screens/Search/Search.tsx:105 msgid "Error:" -msgstr "Erreur :" +msgstr "Erreur :" #: src/view/com/modals/Threadgate.tsx:76 msgid "Everybody" @@ -1220,20 +1222,20 @@ msgstr "Tout le monde" #: src/view/com/modals/ChangeHandle.tsx:150 msgid "Exits handle change process" -msgstr "" +msgstr "Sort du processus de changement de pseudo" #: src/view/com/lightbox/Lightbox.web.tsx:113 msgid "Exits image view" -msgstr "" +msgstr "Sort de la vue de l’image" #: src/view/com/modals/ListAddRemoveUsers.tsx:88 #: src/view/shell/desktop/Search.tsx:182 msgid "Exits inputting search query" -msgstr "" +msgstr "Sort de la saisie de la recherche" #: src/view/com/modals/Waitlist.tsx:138 msgid "Exits signing up for waitlist with {email}" -msgstr "" +msgstr "Sort de l’inscription sur la liste d’attente avec {email}" #: src/view/com/lightbox/Lightbox.web.tsx:156 msgid "Expand alt text" @@ -1242,39 +1244,39 @@ msgstr "Développer le texte alt" #: src/view/com/composer/ComposerReplyTo.tsx:81 #: src/view/com/composer/ComposerReplyTo.tsx:84 msgid "Expand or collapse the full post you are replying to" -msgstr "" +msgstr "Développe ou réduit le post complet auquel vous répondez" #: src/view/com/modals/EmbedConsent.tsx:64 msgid "External Media" -msgstr "" +msgstr "Média externe" #: src/view/com/modals/EmbedConsent.tsx:75 #: src/view/screens/PreferencesExternalEmbeds.tsx:66 msgid "External media may allow websites to collect information about you and your device. No information is sent or requested until you press the \"play\" button." -msgstr "" +msgstr "Les médias externes peuvent permettre à des sites web de collecter des informations sur vous et votre appareil. Aucune information n’est envoyée ou demandée tant que vous n’appuyez pas sur le bouton de lecture." #: src/Navigation.tsx:260 #: src/view/screens/PreferencesExternalEmbeds.tsx:52 #: src/view/screens/Settings.tsx:623 msgid "External Media Preferences" -msgstr "" +msgstr "Préférences sur les médias externes" #: src/view/screens/Settings.tsx:614 msgid "External media settings" -msgstr "" +msgstr "Préférences sur les médias externes" #: src/view/com/modals/AddAppPasswords.tsx:114 #: src/view/com/modals/AddAppPasswords.tsx:118 msgid "Failed to create app password." -msgstr "" +msgstr "Échec de la création du mot de passe d’application." #: src/view/com/modals/CreateOrEditList.tsx:148 msgid "Failed to create the list. Check your internet connection and try again." -msgstr "" +msgstr "Échec de la création de la liste. Vérifiez votre connexion Internet et réessayez." #: src/view/com/util/forms/PostDropdownBtn.tsx:86 msgid "Failed to delete post, please try again" -msgstr "" +msgstr "Échec de la suppression du post, veuillez réessayer" #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:109 #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:141 @@ -1283,11 +1285,11 @@ msgstr "Échec du chargement des fils d’actu recommandés" #: src/Navigation.tsx:194 msgid "Feed" -msgstr "" +msgstr "Fil d’actu" #: src/view/com/feeds/FeedSourceCard.tsx:229 msgid "Feed by {0}" -msgstr "" +msgstr "Fil d’actu par {0}" #: src/view/screens/Feeds.tsx:560 msgid "Feed offline" @@ -1298,7 +1300,7 @@ msgid "Feed Preferences" msgstr "Préférences en matière de fil d’actu" #: src/view/shell/desktop/RightNav.tsx:73 -#: src/view/shell/Drawer.tsx:311 +#: src/view/shell/Drawer.tsx:314 msgid "Feedback" msgstr "Feedback" @@ -1307,32 +1309,32 @@ msgstr "Feedback" #: src/view/screens/Profile.tsx:165 #: src/view/shell/bottom-bar/BottomBar.tsx:181 #: src/view/shell/desktop/LeftNav.tsx:342 -#: src/view/shell/Drawer.tsx:476 -#: src/view/shell/Drawer.tsx:477 +#: src/view/shell/Drawer.tsx:479 +#: src/view/shell/Drawer.tsx:480 msgid "Feeds" msgstr "Fil d’actu" #: src/view/com/auth/onboarding/RecommendedFeeds.tsx:57 msgid "Feeds are created by users to curate content. Choose some feeds that you find interesting." -msgstr "Les fils d’actu sont créés par les utilisateurs pour rassembler du contenu. Choisissez des fils d’actu qui vous intéressent." +msgstr "Les fils d’actu sont créés par d’autres personnes pour rassembler du contenu. Choisissez des fils d’actu qui vous intéressent." #: src/view/screens/SavedFeeds.tsx:156 msgid "Feeds are custom algorithms that users build with a little coding expertise. <0/> for more information." -msgstr "Les fils d’actu sont des algorithmes personnalisés que les utilisateurs construisent avec un peu d’expertise en codage. <0/> pour obtenir de plus amples informations." +msgstr "Les fils d’actu sont des algorithmes personnalisés qui se construisent avec un peu d’expertise en programmation. <0/> pour plus d’informations." #: src/view/com/posts/CustomFeedEmptyState.tsx:47 #: src/view/com/posts/FollowingEmptyState.tsx:57 #: src/view/com/posts/FollowingEndOfFeed.tsx:58 msgid "Find accounts to follow" -msgstr "" +msgstr "Trouver des comptes à suivre" #: src/view/screens/Search/Search.tsx:427 msgid "Find users on Bluesky" -msgstr "Trouver des utilisateurs sur Bluesky" +msgstr "Trouver des comptes sur Bluesky" #: src/view/screens/Search/Search.tsx:425 msgid "Find users with the search tool on the right" -msgstr "Trouvez des utilisateurs à l’aide de l’outil de recherche, à droite" +msgstr "Trouvez des comptes à l’aide de l’outil de recherche, à droite" #: src/view/com/auth/onboarding/RecommendedFollowsItem.tsx:150 msgid "Finding similar accounts..." @@ -1340,25 +1342,25 @@ msgstr "Recherche de comptes similaires…" #: src/view/screens/PreferencesHomeFeed.tsx:111 msgid "Fine-tune the content you see on your home screen." -msgstr "Affinez le contenu de votre écran d’accueil." +msgstr "Affine le contenu affiché sur votre écran d’accueil." #: src/view/screens/PreferencesThreads.tsx:60 msgid "Fine-tune the discussion threads." -msgstr "Affiner les fils de discussion." +msgstr "Affine les fils de discussion." #: src/view/com/modals/EditImage.tsx:115 msgid "Flip horizontal" -msgstr "" +msgstr "Miroir horizontal" #: src/view/com/modals/EditImage.tsx:120 #: src/view/com/modals/EditImage.tsx:287 msgid "Flip vertically" -msgstr "" +msgstr "Miroir vertical" #: src/view/com/profile/FollowButton.tsx:64 msgctxt "action" msgid "Follow" -msgstr "" +msgstr "Suivre" #: src/view/com/profile/ProfileHeader.tsx:552 msgid "Follow" @@ -1366,35 +1368,31 @@ msgstr "Suivre" #: src/view/com/profile/ProfileHeader.tsx:543 msgid "Follow {0}" -msgstr "" +msgstr "Suivre {0}" #: src/view/com/auth/onboarding/RecommendedFollows.tsx:64 msgid "Follow some users to get started. We can recommend you more users based on who you find interesting." -msgstr "Suivez quelques utilisateurs pour commencer. Nous pouvons vous recommander d’autres utilisateurs en fonction des personnes qui vous intéressent." +msgstr "Suivez quelques comptes pour commencer. Nous pouvons vous recommander d’autres comptes en fonction des personnes qui vous intéressent." #: src/view/com/profile/ProfileCard.tsx:194 msgid "Followed by {0}" -msgstr "" +msgstr "Suivi par {0}" #: src/view/com/modals/Threadgate.tsx:98 msgid "Followed users" -msgstr "Utilisateurs suivis" +msgstr "Comptes suivis" #: src/view/screens/PreferencesHomeFeed.tsx:154 msgid "Followed users only" -msgstr "Utilisateurs suivis uniquement" +msgstr "Comptes suivis uniquement" #: src/view/com/notifications/FeedItem.tsx:166 msgid "followed you" -msgstr "" +msgstr "vous suit" #: src/view/screens/ProfileFollowers.tsx:25 msgid "Followers" -msgstr "Followers" - -#: src/view/com/profile/ProfileHeader.tsx:624 -#~ msgid "following" -#~ msgstr "suivi" +msgstr "Abonné·e·s" #: src/view/com/profile/ProfileHeader.tsx:534 #: src/view/screens/ProfileFollows.tsx:25 @@ -1403,7 +1401,7 @@ msgstr "Suivi" #: src/view/com/profile/ProfileHeader.tsx:196 msgid "Following {0}" -msgstr "" +msgstr "Suit {0}" #: src/view/com/profile/ProfileHeader.tsx:585 msgid "Follows you" @@ -1411,7 +1409,7 @@ msgstr "Vous suit" #: src/view/com/profile/ProfileCard.tsx:141 msgid "Follows You" -msgstr "" +msgstr "Vous suit" #: src/view/com/modals/DeleteAccount.tsx:107 msgid "For security reasons, we'll need to send a confirmation code to your email address." @@ -1437,7 +1435,7 @@ msgstr "Mot de passe oublié" #: src/view/com/posts/FeedItem.tsx:188 msgctxt "from-feed" msgid "From <0/>" -msgstr "" +msgstr "Tiré de <0/>" #: src/view/com/composer/photos/SelectPhotoBtn.tsx:43 msgid "Gallery" @@ -1472,8 +1470,12 @@ msgstr "Aller à la suite" msgid "Handle" msgstr "Pseudo" +#: src/view/com/auth/create/CreateAccount.tsx:190 +msgid "Having trouble?" +msgstr "" + #: src/view/shell/desktop/RightNav.tsx:102 -#: src/view/shell/Drawer.tsx:321 +#: src/view/shell/Drawer.tsx:324 msgid "Help" msgstr "Aide" @@ -1485,7 +1487,7 @@ msgstr "Voici le mot de passe de votre appli." #: src/view/com/notifications/FeedItem.tsx:329 msgctxt "action" msgid "Hide" -msgstr "" +msgstr "Cacher" #: src/view/com/modals/ContentFilteringSettings.tsx:246 #: src/view/com/util/moderation/ContentHider.tsx:105 @@ -1500,50 +1502,50 @@ msgstr "Cacher ce post" #: src/view/com/util/moderation/ContentHider.tsx:67 #: src/view/com/util/moderation/PostHider.tsx:61 msgid "Hide the content" -msgstr "" +msgstr "Cacher ce contenu" #: src/view/com/util/forms/PostDropdownBtn.tsx:189 msgid "Hide this post?" -msgstr "Cacher ce post ?" +msgstr "Cacher ce post ?" #: src/view/com/notifications/FeedItem.tsx:319 msgid "Hide user list" -msgstr "Cacher la liste des utilisateurs" +msgstr "Cacher la liste des comptes" #: src/view/com/profile/ProfileHeader.tsx:526 msgid "Hides posts from {0} in your feed" -msgstr "" +msgstr "Masque les posts de {0} dans votre fil d’actu" #: src/view/com/posts/FeedErrorMessage.tsx:111 msgid "Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue." -msgstr "Hmm, un problème s’est produit avec le serveur de fils d’actu. Veuillez informer le propriétaire du fil d’actu de ce problème." +msgstr "Hmm, un problème s’est produit avec le serveur de fils d’actu. Veuillez informer la personne propriétaire du fil d’actu de ce problème." #: src/view/com/posts/FeedErrorMessage.tsx:99 msgid "Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue." -msgstr "Hmm, le serveur du fils d’actu semble être mal configuré. Veuillez informer le propriétaire du fil d’actu de ce problème." +msgstr "Hmm, le serveur du fils d’actu semble être mal configuré. Veuillez informer la personne propriétaire du fil d’actu de ce problème." #: src/view/com/posts/FeedErrorMessage.tsx:105 msgid "Hmm, the feed server appears to be offline. Please let the feed owner know about this issue." -msgstr "Mmm… le serveur de fils d’actu semble être hors ligne. Veuillez informer le propriétaire du fil d’actu de ce problème." +msgstr "Mmm… le serveur de fils d’actu semble être hors ligne. Veuillez informer la personne propriétaire du fil d’actu de ce problème." #: src/view/com/posts/FeedErrorMessage.tsx:102 msgid "Hmm, the feed server gave a bad response. Please let the feed owner know about this issue." -msgstr "Mmm… le serveur de fils d’actu ne répond pas. Veuillez informer le propriétaire du fil d’actu de ce problème." +msgstr "Mmm… le serveur de fils d’actu ne répond pas. Veuillez informer la personne propriétaire du fil d’actu de ce problème." #: src/view/com/posts/FeedErrorMessage.tsx:96 msgid "Hmm, we're having trouble finding this feed. It may have been deleted." -msgstr "Hmm, nous n’arrivons pas à trouver ce fils d’actu. Il a peut-être été supprimé." +msgstr "Hmm, nous n’arrivons pas à trouver ce fil d’actu. Il a peut-être été supprimé." #: src/Navigation.tsx:432 #: src/view/shell/bottom-bar/BottomBar.tsx:137 #: src/view/shell/desktop/LeftNav.tsx:306 -#: src/view/shell/Drawer.tsx:398 -#: src/view/shell/Drawer.tsx:399 +#: src/view/shell/Drawer.tsx:401 +#: src/view/shell/Drawer.tsx:402 msgid "Home" msgstr "Accueil" #: src/Navigation.tsx:249 -#: src/view/com/pager/FeedsTabBarMobile.tsx:98 +#: src/view/com/pager/FeedsTabBarMobile.tsx:117 #: src/view/screens/PreferencesHomeFeed.tsx:104 #: src/view/screens/Settings.tsx:509 msgid "Home Feed Preferences" @@ -1551,12 +1553,12 @@ msgstr "Préférences de fils d’actu de l’accueil" #: src/view/com/auth/login/ForgotPasswordForm.tsx:116 msgid "Hosting provider" -msgstr "Fournisseur d’hébergement" +msgstr "Hébergeur" #: src/view/com/auth/create/Step1.tsx:76 #: src/view/com/auth/create/Step1.tsx:81 -msgid "Hosting provider address" -msgstr "Adresse de l’hébergeur" +#~ msgid "Hosting provider address" +#~ msgstr "Adresse de l’hébergeur" #: src/view/com/modals/InAppBrowserConsent.tsx:44 msgid "How should we open this link?" @@ -1568,7 +1570,7 @@ msgstr "J’ai un code" #: src/view/com/modals/VerifyEmail.tsx:216 msgid "I have a confirmation code" -msgstr "" +msgstr "J’ai un code de confirmation" #: src/view/com/modals/ChangeHandle.tsx:283 msgid "I have my own domain" @@ -1576,7 +1578,7 @@ msgstr "J’ai mon propre domaine" #: src/view/com/lightbox/Lightbox.web.tsx:158 msgid "If alt text is long, toggles alt text expanded state" -msgstr "" +msgstr "Si le texte alternatif est trop long, change son mode d’affichage" #: src/view/com/modals/SelfLabel.tsx:127 msgid "If none are selected, suitable for all ages." @@ -1584,7 +1586,7 @@ msgstr "Si rien n’est sélectionné, il n’y a pas de restriction d’âge." #: src/view/com/util/images/Gallery.tsx:37 msgid "Image" -msgstr "" +msgstr "Image" #: src/view/com/modals/AltImage.tsx:97 msgid "Image alt text" @@ -1597,63 +1599,75 @@ msgstr "Options d’images" #: src/view/com/auth/login/SetNewPasswordForm.tsx:110 msgid "Input code sent to your email for password reset" -msgstr "" +msgstr "Entrez le code envoyé à votre e-mail pour réinitialiser le mot de passe" #: src/view/com/modals/DeleteAccount.tsx:180 msgid "Input confirmation code for account deletion" +msgstr "Entrez le code de confirmation pour supprimer le compte" + +#: src/view/com/auth/create/Step1.tsx:144 +msgid "Input email for Bluesky account" msgstr "" #: src/view/com/auth/create/Step2.tsx:109 -msgid "Input email for Bluesky waitlist" -msgstr "" +#~ msgid "Input email for Bluesky waitlist" +#~ msgstr "Entrez l’e-mail pour la liste d’attente de Bluesky" #: src/view/com/auth/create/Step1.tsx:80 -msgid "Input hosting provider address" -msgstr "" +#~ msgid "Input hosting provider address" +#~ msgstr "Entrez l’adresse de l’hébergeur" -#: src/view/com/auth/create/Step2.tsx:73 +#: src/view/com/auth/create/Step1.tsx:102 msgid "Input invite code to proceed" -msgstr "" +msgstr "Entrez le code d’invitation pour continuer" #: src/view/com/modals/AddAppPasswords.tsx:182 msgid "Input name for app password" -msgstr "" +msgstr "Entrez le nom du mot de passe de l’appli" #: src/view/com/auth/login/SetNewPasswordForm.tsx:133 msgid "Input new password" -msgstr "" +msgstr "Entrez le nouveau mot de passe" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" +msgstr "Entrez le mot de passe pour la suppression du compte" + +#: src/view/com/auth/create/Step2.tsx:92 +msgid "Input phone number for SMS verification" msgstr "" #: src/view/com/auth/login/LoginForm.tsx:227 msgid "Input the password tied to {identifier}" -msgstr "" +msgstr "Entrez le mot de passe associé à {identifier}" #: src/view/com/auth/login/LoginForm.tsx:194 msgid "Input the username or email address you used at signup" +msgstr "Entrez le pseudo ou l’adresse e-mail que vous avez utilisé lors de l’inscription" + +#: src/view/com/auth/create/Step2.tsx:166 +msgid "Input the verification code we have texted to you" msgstr "" #: src/view/com/modals/Waitlist.tsx:90 msgid "Input your email to get on the Bluesky waitlist" -msgstr "" +msgstr "Entrez votre e-mail pour vous inscrire sur la liste d’attente de Bluesky" #: src/view/com/auth/login/LoginForm.tsx:226 msgid "Input your password" -msgstr "" +msgstr "Entrez votre mot de passe" #: src/view/com/auth/create/Step3.tsx:39 msgid "Input your user handle" -msgstr "" +msgstr "Entrez votre pseudo" #: src/view/com/post-thread/PostThreadItem.tsx:229 msgid "Invalid or unsupported post record" -msgstr "" +msgstr "Enregistrement de post invalide ou non pris en charge" #: src/view/com/auth/login/LoginForm.tsx:115 msgid "Invalid username or password" -msgstr "Nom d’utilisateur ou mot de passe incorrect" +msgstr "Pseudo ou mot de passe incorrect" #: src/view/screens/Settings.tsx:411 msgid "Invite" @@ -1664,26 +1678,26 @@ msgstr "Inviter" msgid "Invite a Friend" msgstr "Inviter un ami" -#: src/view/com/auth/create/Step2.tsx:63 -#: src/view/com/auth/create/Step2.tsx:72 +#: src/view/com/auth/create/Step1.tsx:92 +#: src/view/com/auth/create/Step1.tsx:101 msgid "Invite code" msgstr "Code d’invitation" -#: src/view/com/auth/create/state.ts:136 +#: src/view/com/auth/create/state.ts:193 msgid "Invite code not accepted. Check that you input it correctly and try again." msgstr "Code d’invitation refusé. Vérifiez que vous l’avez saisi correctement et réessayez." #: src/view/com/modals/InviteCodes.tsx:170 msgid "Invite codes: {0} available" -msgstr "" +msgstr "Code d’invitation : {0} disponible" -#: src/view/shell/Drawer.tsx:642 +#: src/view/shell/Drawer.tsx:645 msgid "Invite codes: {invitesAvailable} available" -msgstr "Codes d’invitation : {invitesAvailable} disponible" +msgstr "Invitations : {invitesAvailable} codes dispo" #: src/view/com/modals/InviteCodes.tsx:169 msgid "Invite codes: 1 available" -msgstr "" +msgstr "Invitations : 1 code dispo" #: src/view/com/auth/HomeLoggedOutCTA.tsx:99 msgid "Jobs" @@ -1693,8 +1707,8 @@ msgstr "Emplois" msgid "Join the waitlist" msgstr "S’inscrire sur la liste d’attente" -#: src/view/com/auth/create/Step2.tsx:86 -#: src/view/com/auth/create/Step2.tsx:90 +#: src/view/com/auth/create/Step1.tsx:118 +#: src/view/com/auth/create/Step1.tsx:122 msgid "Join the waitlist." msgstr "S’inscrire sur la liste d’attente." @@ -1708,7 +1722,7 @@ msgstr "Sélection de la langue" #: src/view/screens/Settings.tsx:560 msgid "Language settings" -msgstr "" +msgstr "Préférences de langue" #: src/Navigation.tsx:141 #: src/view/screens/LanguageSettings.tsx:89 @@ -1719,9 +1733,9 @@ msgstr "Paramètres linguistiques" msgid "Languages" msgstr "Langues" -#: src/view/com/auth/create/StepHeader.tsx:13 +#: src/view/com/auth/create/StepHeader.tsx:20 msgid "Last step!" -msgstr "" +msgstr "Dernière étape !" #: src/view/com/util/moderation/ContentHider.tsx:103 msgid "Learn more" @@ -1755,12 +1769,12 @@ msgstr "Quitter Bluesky" #: src/view/screens/Settings.tsx:280 msgid "Legacy storage cleared, you need to restart the app now." -msgstr "" +msgstr "Stockage ancien effacé, vous devez redémarrer l’application maintenant." #: src/view/com/auth/login/Login.tsx:128 #: src/view/com/auth/login/Login.tsx:144 msgid "Let's get your password reset!" -msgstr "Réinitialisez votre mot de passe !" +msgstr "Réinitialisez votre mot de passe !" #: src/view/com/util/UserAvatar.tsx:245 #: src/view/com/util/UserBanner.tsx:60 @@ -1769,11 +1783,11 @@ msgstr "Bibliothèque" #: src/view/screens/Settings.tsx:473 msgid "Light" -msgstr "" +msgstr "Clair" #: src/view/com/util/post-ctrls/PostCtrls.tsx:189 msgid "Like" -msgstr "" +msgstr "Liker" #: src/view/screens/ProfileFeed.tsx:600 msgid "Like this feed" @@ -1787,19 +1801,19 @@ msgstr "Liké par" #: src/view/com/feeds/FeedSourceCard.tsx:277 msgid "Liked by {0} {1}" -msgstr "" +msgstr "Liké par {0} {1}" #: src/view/screens/ProfileFeed.tsx:615 msgid "Liked by {likeCount} {0}" -msgstr "" +msgstr "Liké par {likeCount} {0}" #: src/view/com/notifications/FeedItem.tsx:171 msgid "liked your custom feed{0}" -msgstr "" +msgstr "liké votre fil d’actu personnalisé{0}" #: src/view/com/notifications/FeedItem.tsx:155 msgid "liked your post" -msgstr "" +msgstr "liké votre post" #: src/view/screens/Profile.tsx:164 msgid "Likes" @@ -1807,11 +1821,11 @@ msgstr "Likes" #: src/view/com/post-thread/PostThreadItem.tsx:184 msgid "Likes on this post" -msgstr "" +msgstr "Likes sur ce post" #: src/Navigation.tsx:168 msgid "List" -msgstr "" +msgstr "Liste" #: src/view/com/modals/CreateOrEditList.tsx:205 msgid "List Avatar" @@ -1819,19 +1833,19 @@ msgstr "Liste des avatars" #: src/view/screens/ProfileList.tsx:323 msgid "List blocked" -msgstr "" +msgstr "Liste bloquée" #: src/view/com/feeds/FeedSourceCard.tsx:231 msgid "List by {0}" -msgstr "" +msgstr "Liste par {0}" #: src/view/screens/ProfileList.tsx:367 msgid "List deleted" -msgstr "" +msgstr "Liste supprimée" #: src/view/screens/ProfileList.tsx:282 msgid "List muted" -msgstr "" +msgstr "Liste masquée" #: src/view/com/modals/CreateOrEditList.tsx:218 msgid "List Name" @@ -1839,17 +1853,17 @@ msgstr "Nom de liste" #: src/view/screens/ProfileList.tsx:342 msgid "List unblocked" -msgstr "" +msgstr "Liste débloquée" #: src/view/screens/ProfileList.tsx:301 msgid "List unmuted" -msgstr "" +msgstr "Liste démasquée" #: src/Navigation.tsx:111 #: src/view/screens/Profile.tsx:166 #: src/view/shell/desktop/LeftNav.tsx:379 -#: src/view/shell/Drawer.tsx:492 -#: src/view/shell/Drawer.tsx:493 +#: src/view/shell/Drawer.tsx:495 +#: src/view/shell/Drawer.tsx:496 msgid "Lists" msgstr "Listes" @@ -1879,7 +1893,7 @@ msgstr "Serveur de dév local" #: src/Navigation.tsx:209 msgid "Log" -msgstr "" +msgstr "Journaux" #: src/view/screens/Moderation.tsx:136 msgid "Logged-out visibility" @@ -1891,7 +1905,7 @@ msgstr "Se connecter à un compte qui n’est pas listé" #: src/view/com/modals/LinkWarning.tsx:65 msgid "Make sure this is where you intend to go!" -msgstr "Assurez-vous que c’est bien là que vous avez l’intention d’aller !" +msgstr "Assurez-vous que c’est bien là que vous avez l’intention d’aller !" #: src/view/screens/Profile.tsx:163 msgid "Media" @@ -1899,56 +1913,52 @@ msgstr "Média" #: src/view/com/threadgate/WhoCanReply.tsx:139 msgid "mentioned users" -msgstr "utilisateurs mentionnés" +msgstr "comptes mentionnés" #: src/view/com/modals/Threadgate.tsx:93 msgid "Mentioned users" -msgstr "Utilisateurs mentionnés" +msgstr "Comptes mentionnés" #: src/view/com/util/ViewHeader.tsx:81 #: src/view/screens/Search/Search.tsx:537 msgid "Menu" msgstr "Menu" -#: src/view/com/posts/FeedErrorMessage.tsx:194 -#~ msgid "Message from server" -#~ msgstr "Message du serveur" - #: src/view/com/posts/FeedErrorMessage.tsx:197 msgid "Message from server: {0}" -msgstr "" +msgstr "Message du serveur : {0}" #: src/Navigation.tsx:116 #: src/view/screens/Moderation.tsx:64 #: src/view/screens/Settings.tsx:591 #: src/view/shell/desktop/LeftNav.tsx:397 -#: src/view/shell/Drawer.tsx:511 -#: src/view/shell/Drawer.tsx:512 +#: src/view/shell/Drawer.tsx:514 +#: src/view/shell/Drawer.tsx:515 msgid "Moderation" msgstr "Modération" #: src/view/com/lists/ListCard.tsx:92 #: src/view/com/modals/UserAddRemoveLists.tsx:190 msgid "Moderation list by {0}" -msgstr "" +msgstr "Liste de modération par {0}" #: src/view/screens/ProfileList.tsx:753 msgid "Moderation list by <0/>" -msgstr "" +msgstr "Liste de modération par <0/>" #: src/view/com/lists/ListCard.tsx:90 #: src/view/com/modals/UserAddRemoveLists.tsx:188 #: src/view/screens/ProfileList.tsx:751 msgid "Moderation list by you" -msgstr "" +msgstr "Liste de modération par vous" #: src/view/com/modals/CreateOrEditList.tsx:139 msgid "Moderation list created" -msgstr "" +msgstr "Liste de modération créée" #: src/view/com/modals/CreateOrEditList.tsx:126 msgid "Moderation list updated" -msgstr "" +msgstr "Liste de modération mise à jour" #: src/view/screens/Moderation.tsx:95 msgid "Moderation lists" @@ -1961,11 +1971,11 @@ msgstr "Listes de modération" #: src/view/screens/Settings.tsx:585 msgid "Moderation settings" -msgstr "" +msgstr "Paramètres de modération" #: src/view/com/modals/ModerationDetails.tsx:35 msgid "Moderator has chosen to set a general warning on the content." -msgstr "" +msgstr "La modération a choisi d’ajouter un avertissement général sur le contenu." #: src/view/shell/desktop/Feeds.tsx:53 msgid "More feeds" @@ -1979,7 +1989,7 @@ msgstr "Plus d’options" #: src/view/com/util/forms/PostDropdownBtn.tsx:268 msgid "More post options" -msgstr "" +msgstr "Plus d’options de post" #: src/view/screens/PreferencesThreads.tsx:82 msgid "Most-liked replies first" @@ -1999,16 +2009,20 @@ msgstr "Masquer la liste" #: src/view/screens/ProfileList.tsx:274 msgid "Mute these accounts?" -msgstr "Masquer ces comptes ?" +msgstr "Masquer ces comptes ?" #: src/view/screens/ProfileList.tsx:278 msgid "Mute this List" -msgstr "" +msgstr "Masquer cette liste" #: src/view/com/util/forms/PostDropdownBtn.tsx:169 msgid "Mute thread" msgstr "Masquer ce fil de discussion" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "Comptes masqués" @@ -2049,22 +2063,22 @@ msgstr "Nom" #: src/view/com/modals/CreateOrEditList.tsx:108 msgid "Name is required" -msgstr "" +msgstr "Le nom est requis" #: src/view/com/auth/login/ForgotPasswordForm.tsx:186 #: src/view/com/auth/login/LoginForm.tsx:286 #: src/view/com/auth/login/SetNewPasswordForm.tsx:166 msgid "Navigates to the next screen" -msgstr "" +msgstr "Navigue vers le prochain écran" -#: src/view/shell/Drawer.tsx:71 +#: src/view/shell/Drawer.tsx:73 msgid "Navigates to your profile" -msgstr "" +msgstr "Navigue vers votre profil" #: src/view/com/modals/EmbedConsent.tsx:107 #: src/view/com/modals/EmbedConsent.tsx:123 msgid "Never load embeds from {0}" -msgstr "" +msgstr "Ne jamais charger les contenus intégrés de {0}" #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:72 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:72 @@ -2074,7 +2088,7 @@ msgstr "Ne perdez jamais l’accès à vos followers et à vos données." #: src/view/screens/Lists.tsx:76 msgctxt "action" msgid "New" -msgstr "" +msgstr "Nouveau" #: src/view/screens/ModerationModlists.tsx:78 msgid "New" @@ -2082,16 +2096,16 @@ msgstr "Nouveau" #: src/view/com/modals/CreateOrEditList.tsx:195 msgid "New Moderation List" -msgstr "" +msgstr "Nouvelle liste de modération" #: src/view/com/auth/login/SetNewPasswordForm.tsx:122 msgid "New password" -msgstr "" +msgstr "Nouveau mot de passe" #: src/view/com/feeds/FeedPage.tsx:201 msgctxt "action" msgid "New post" -msgstr "" +msgstr "Nouveau post" #: src/view/screens/Feeds.tsx:511 #: src/view/screens/Profile.tsx:354 @@ -2105,26 +2119,22 @@ msgstr "Nouveau post" #: src/view/shell/desktop/LeftNav.tsx:258 msgctxt "action" msgid "New Post" -msgstr "" - -#: src/view/shell/desktop/LeftNav.tsx:258 -#~ msgid "New Post" -#~ msgstr "Nouveau post" +msgstr "Nouveau post" #: src/view/com/modals/CreateOrEditList.tsx:190 msgid "New User List" -msgstr "" +msgstr "Nouvelle liste de comptes" #: src/view/screens/PreferencesThreads.tsx:79 msgid "Newest replies first" -msgstr "" +msgstr "Réponses les plus récentes en premier" #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 msgctxt "action" msgid "Next" -msgstr "" +msgstr "Suivant" -#: src/view/com/auth/create/CreateAccount.tsx:154 +#: src/view/com/auth/create/CreateAccount.tsx:155 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 #: src/view/com/auth/login/LoginForm.tsx:288 @@ -2154,11 +2164,11 @@ msgstr "Aucune description" #: src/view/com/profile/ProfileHeader.tsx:217 msgid "No longer following {0}" -msgstr "" +msgstr "Ne suit plus {0}" #: src/view/com/notifications/Feed.tsx:107 msgid "No notifications yet!" -msgstr "" +msgstr "Pas encore de notifications !" #: src/view/com/composer/text-input/mobile/Autocomplete.tsx:97 #: src/view/com/composer/text-input/web/Autocomplete.tsx:191 @@ -2167,7 +2177,7 @@ msgstr "Aucun résultat" #: src/view/screens/Feeds.tsx:456 msgid "No results found for \"{query}\"" -msgstr "Aucun résultat trouvé pour « {query} »" +msgstr "Aucun résultat trouvé pour « {query} »" #: src/view/com/modals/ListAddRemoveUsers.tsx:127 #: src/view/screens/Search/Search.tsx:270 @@ -2179,7 +2189,7 @@ msgstr "Aucun résultat trouvé pour {query}" #: src/view/com/modals/EmbedConsent.tsx:129 msgid "No thanks" -msgstr "" +msgstr "Non merci" #: src/view/com/modals/Threadgate.tsx:82 msgid "Nobody" @@ -2191,34 +2201,34 @@ msgstr "Sans objet." #: src/Navigation.tsx:106 msgid "Not Found" -msgstr "" +msgstr "Introuvable" #: src/view/com/modals/VerifyEmail.tsx:246 #: src/view/com/modals/VerifyEmail.tsx:252 msgid "Not right now" -msgstr "" +msgstr "Pas maintenant" #: src/view/screens/Moderation.tsx:232 msgid "Note: Bluesky is an open and public network. This setting only limits the visibility of your content on the Bluesky app and website, and other apps may not respect this setting. Your content may still be shown to logged-out users by other apps and websites." -msgstr "Remarque : Bluesky est un réseau ouvert et public. Ce paramètre limite uniquement la visibilité de votre contenu sur l’application et le site Web de Bluesky, et d’autres applications peuvent ne pas respecter ce paramètre. Votre contenu peut toujours être montré aux utilisateurs déconnectés par d’autres applications et sites Web." +msgstr "Remarque : Bluesky est un réseau ouvert et public. Ce paramètre limite uniquement la visibilité de votre contenu sur l’application et le site Web de Bluesky, et d’autres applications peuvent ne pas respecter ce paramètre. Votre contenu peut toujours être montré aux personnes non connectées par d’autres applications et sites Web." #: src/Navigation.tsx:447 #: src/view/screens/Notifications.tsx:113 #: src/view/screens/Notifications.tsx:137 #: src/view/shell/bottom-bar/BottomBar.tsx:205 #: src/view/shell/desktop/LeftNav.tsx:361 -#: src/view/shell/Drawer.tsx:435 -#: src/view/shell/Drawer.tsx:436 +#: src/view/shell/Drawer.tsx:438 +#: src/view/shell/Drawer.tsx:439 msgid "Notifications" msgstr "Notifications" #: src/view/com/modals/SelfLabel.tsx:103 msgid "Nudity" -msgstr "" +msgstr "Nudité" #: src/view/com/util/ErrorBoundary.tsx:34 msgid "Oh no!" -msgstr "Oh non !" +msgstr "Oh non !" #: src/view/com/auth/login/PasswordUpdatedForm.tsx:41 msgid "Okay" @@ -2230,7 +2240,7 @@ msgstr "Plus anciennes réponses en premier" #: src/view/screens/Settings.tsx:236 msgid "Onboarding reset" -msgstr "" +msgstr "Réinitialiser le didacticiel" #: src/view/com/composer/Composer.tsx:375 msgid "One or more images is missing alt text." @@ -2244,7 +2254,7 @@ msgstr "Seul {0} peut répondre." #: src/view/com/modals/ProfilePreview.tsx:61 #: src/view/screens/AppPasswords.tsx:65 msgid "Oops!" -msgstr "" +msgstr "Oups !" #: src/view/com/composer/Composer.tsx:470 #: src/view/com/composer/Composer.tsx:471 @@ -2255,33 +2265,33 @@ msgstr "Ouvrir le sélecteur d’emoji" msgid "Open links with in-app browser" msgstr "" -#: src/view/com/pager/FeedsTabBarMobile.tsx:76 +#: src/view/com/pager/FeedsTabBarMobile.tsx:81 msgid "Open navigation" msgstr "Navigation ouverte" #: src/view/screens/Settings.tsx:737 msgid "Open storybook page" -msgstr "" +msgstr "Ouvrir la page Storybook" #: src/view/com/util/forms/DropdownButton.tsx:147 msgid "Opens {numItems} options" -msgstr "" +msgstr "Ouvre {numItems} options" #: src/view/screens/Log.tsx:54 msgid "Opens additional details for a debug entry" -msgstr "" +msgstr "Ouvre des détails supplémentaires pour une entrée de débug" #: src/view/com/notifications/FeedItem.tsx:352 msgid "Opens an expanded list of users in this notification" -msgstr "" +msgstr "Ouvre une liste étendue des comptes dans cette notification" #: src/view/com/composer/photos/OpenCameraBtn.tsx:61 msgid "Opens camera on device" -msgstr "" +msgstr "Ouvre l’appareil photo de l’appareil" #: src/view/com/composer/Prompt.tsx:25 msgid "Opens composer" -msgstr "" +msgstr "Ouvre le rédacteur" #: src/view/screens/Settings.tsx:561 msgid "Opens configurable language settings" @@ -2289,37 +2299,37 @@ msgstr "Ouvre les paramètres linguistiques configurables" #: src/view/com/composer/photos/SelectPhotoBtn.tsx:44 msgid "Opens device photo gallery" -msgstr "" +msgstr "Ouvre la galerie de photos de l’appareil" #: src/view/com/profile/ProfileHeader.tsx:459 msgid "Opens editor for profile display name, avatar, background image, and description" -msgstr "" +msgstr "Ouvre l’éditeur pour le nom d’affichage du profil, l’avatar, l’image d’arrière-plan et la description" #: src/view/screens/Settings.tsx:615 msgid "Opens external embeds settings" -msgstr "" +msgstr "Ouvre les paramètres d’intégration externe" #: src/view/com/profile/ProfileHeader.tsx:614 msgid "Opens followers list" -msgstr "" +msgstr "Ouvre la liste des comptes abonnés" #: src/view/com/profile/ProfileHeader.tsx:633 msgid "Opens following list" -msgstr "" +msgstr "Ouvre la liste des abonnements" #: src/view/screens/Settings.tsx:412 msgid "Opens invite code list" -msgstr "" +msgstr "Ouvre la liste des codes d’invitation" #: src/view/com/modals/InviteCodes.tsx:172 #: src/view/shell/desktop/RightNav.tsx:156 -#: src/view/shell/Drawer.tsx:643 +#: src/view/shell/Drawer.tsx:646 msgid "Opens list of invite codes" msgstr "Ouvre la liste des codes d’invitation" #: src/view/screens/Settings.tsx:696 msgid "Opens modal for account deletion confirmation. Requires email code." -msgstr "" +msgstr "Ouvre la fenêtre modale pour confirmer la suppression du compte. Requiert un code e-mail." #: src/view/com/modals/ChangeHandle.tsx:281 msgid "Opens modal for using custom domain" @@ -2331,11 +2341,11 @@ msgstr "Ouvre les paramètres de modération" #: src/view/com/auth/login/LoginForm.tsx:236 msgid "Opens password reset form" -msgstr "" +msgstr "Ouvre le formulaire de réinitialisation du mot de passe" #: src/view/screens/Feeds.tsx:335 msgid "Opens screen to edit Saved Feeds" -msgstr "" +msgstr "Ouvre l’écran pour modifier les fils d’actu enregistrés" #: src/view/screens/Settings.tsx:542 msgid "Opens screen with all saved feeds" @@ -2363,11 +2373,11 @@ msgstr "Ouvre les préférences relatives aux fils de discussion" #: src/view/com/util/forms/DropdownButton.tsx:254 msgid "Option {0} of {numItems}" -msgstr "" +msgstr "Option {0} sur {numItems}" #: src/view/com/modals/Threadgate.tsx:89 msgid "Or combine these options:" -msgstr "Ou une combinaison de ces options :" +msgstr "Ou une combinaison de ces options :" #: src/view/com/auth/login/ChooseAccountForm.tsx:138 msgid "Other account" @@ -2386,11 +2396,11 @@ msgstr "Autre…" msgid "Page not found" msgstr "Page introuvable" -#: src/view/com/auth/create/Step2.tsx:122 -#: src/view/com/auth/create/Step2.tsx:132 +#: src/view/com/auth/create/Step1.tsx:158 +#: src/view/com/auth/create/Step1.tsx:168 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "Mot de passe" @@ -2400,22 +2410,26 @@ msgstr "Mise à jour du mot de passe" #: src/view/com/auth/login/PasswordUpdatedForm.tsx:28 msgid "Password updated!" -msgstr "Mot de passe mis à jour !" +msgstr "Mot de passe mis à jour !" #: src/Navigation.tsx:162 msgid "People followed by @{0}" -msgstr "" +msgstr "Personnes suivies par @{0}" #: src/Navigation.tsx:155 msgid "People following @{0}" -msgstr "" +msgstr "Personnes qui suivent @{0}" #: src/view/com/lightbox/Lightbox.tsx:66 msgid "Permission to access camera roll is required." -msgstr "" +msgstr "Permission d’accès à la pellicule requise." #: src/view/com/lightbox/Lightbox.tsx:72 msgid "Permission to access camera roll was denied. Please enable it in your system settings." +msgstr "Permission d’accès à la pellicule refusée. Veuillez l’activer dans les paramètres de votre système." + +#: src/view/com/auth/create/Step2.tsx:79 +msgid "Phone number" msgstr "" #: src/view/com/modals/SelfLabel.tsx:121 @@ -2425,7 +2439,7 @@ msgstr "Images destinées aux adultes." #: src/view/screens/ProfileFeed.tsx:362 #: src/view/screens/ProfileList.tsx:559 msgid "Pin to home" -msgstr "" +msgstr "Ajouter à l’accueil" #: src/view/screens/SavedFeeds.tsx:88 msgid "Pinned Feeds" @@ -2433,22 +2447,22 @@ msgstr "Fils épinglés" #: src/view/com/util/post-embeds/ExternalGifEmbed.tsx:111 msgid "Play {0}" -msgstr "" +msgstr "Lire {0}" #: src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx:54 #: src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx:55 msgid "Play Video" -msgstr "" +msgstr "Lire la vidéo" #: src/view/com/util/post-embeds/ExternalGifEmbed.tsx:110 msgid "Plays the GIF" -msgstr "" +msgstr "Lit le GIF" -#: src/view/com/auth/create/state.ts:116 +#: src/view/com/auth/create/state.ts:171 msgid "Please choose your handle." msgstr "Veuillez choisir votre pseudo." -#: src/view/com/auth/create/state.ts:109 +#: src/view/com/auth/create/state.ts:154 msgid "Please choose your password." msgstr "Veuillez choisir votre mot de passe." @@ -2458,31 +2472,40 @@ msgstr "Veuillez confirmer votre e-mail avant de le modifier. Ceci est temporair #: src/view/com/modals/AddAppPasswords.tsx:89 msgid "Please enter a name for your app password. All spaces is not allowed." +msgstr "Veuillez entrer un nom pour votre mot de passe d’application. Les espaces ne sont pas autorisés." + +#: src/view/com/auth/create/Step2.tsx:102 +msgid "Please enter a phone number that can receive SMS text messages." msgstr "" #: src/view/com/modals/AddAppPasswords.tsx:144 msgid "Please enter a unique name for this App Password or use our randomly generated one." msgstr "Veuillez saisir un nom unique pour le mot de passe de l’application ou utiliser celui que nous avons généré de manière aléatoire." -#: src/view/com/auth/create/state.ts:95 +#: src/view/com/auth/create/state.ts:164 +msgid "Please enter the code you received by SMS." +msgstr "" + +#: src/view/com/auth/create/Step2.tsx:177 +msgid "Please enter the verification code sent to" +msgstr "" + +#: src/view/com/auth/create/state.ts:140 msgid "Please enter your email." msgstr "Veuillez entrer votre e-mail." -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" -msgstr "Veuillez également entrer votre mot de passe :" +msgstr "Veuillez également entrer votre mot de passe :" #: src/view/com/modals/AppealLabel.tsx:72 #: src/view/com/modals/AppealLabel.tsx:75 msgid "Please tell us why you think this content warning was incorrectly applied!" -msgstr "Dites-nous donc pourquoi vous pensez que cet avertissement de contenu a été appliqué à tort !" - -#~ msgid "Please tell us why you think this decision was incorrect." -#~ msgstr "Dites-nous pourquoi vous pensez que cette décision était incorrecte." +msgstr "Dites-nous donc pourquoi vous pensez que cet avertissement de contenu a été appliqué à tort !" #: src/view/com/modals/VerifyEmail.tsx:101 msgid "Please Verify Your Email" -msgstr "" +msgstr "Veuillez vérifier votre e-mail" #: src/view/com/composer/Composer.tsx:215 msgid "Please wait for your link card to finish loading" @@ -2490,43 +2513,37 @@ msgstr "Veuillez patienter le temps que votre carte de lien soit chargée" #: src/view/com/modals/SelfLabel.tsx:111 msgid "Porn" -msgstr "" +msgstr "Porno" #: src/view/com/composer/Composer.tsx:350 #: src/view/com/composer/Composer.tsx:358 msgctxt "action" msgid "Post" -msgstr "" +msgstr "Poster" #: src/view/com/post-thread/PostThread.tsx:227 #: src/view/screens/PostThread.tsx:82 msgctxt "description" msgid "Post" -msgstr "" - -#: src/view/com/composer/Composer.tsx:346 -#: src/view/com/post-thread/PostThread.tsx:225 -#: src/view/screens/PostThread.tsx:80 -#~ msgid "Post" -#~ msgstr "Post" +msgstr "Post" #: src/view/com/post-thread/PostThreadItem.tsx:176 msgid "Post by {0}" -msgstr "" +msgstr "Post de {0}" #: src/Navigation.tsx:174 #: src/Navigation.tsx:181 #: src/Navigation.tsx:188 msgid "Post by @{0}" -msgstr "" +msgstr "Post de @{0}" #: src/view/com/util/forms/PostDropdownBtn.tsx:82 msgid "Post deleted" -msgstr "" +msgstr "Post supprimé" #: src/view/com/post-thread/PostThread.tsx:382 msgid "Post hidden" -msgstr "Posts cachés" +msgstr "Post caché" #: src/view/com/composer/select-language/SelectLangBtn.tsx:87 msgid "Post language" @@ -2546,7 +2563,7 @@ msgstr "Posts" #: src/view/com/posts/FeedErrorMessage.tsx:64 msgid "Posts hidden" -msgstr "" +msgstr "Posts cachés" #: src/view/com/modals/LinkWarning.tsx:46 msgid "Potentially Misleading Link" @@ -2572,7 +2589,7 @@ msgstr "Vie privée" #: src/Navigation.tsx:219 #: src/view/screens/PrivacyPolicy.tsx:29 #: src/view/screens/Settings.tsx:824 -#: src/view/shell/Drawer.tsx:262 +#: src/view/shell/Drawer.tsx:265 msgid "Privacy Policy" msgstr "Charte de confidentialité" @@ -2582,15 +2599,15 @@ msgstr "Traitement…" #: src/view/shell/bottom-bar/BottomBar.tsx:247 #: src/view/shell/desktop/LeftNav.tsx:415 -#: src/view/shell/Drawer.tsx:70 -#: src/view/shell/Drawer.tsx:546 -#: src/view/shell/Drawer.tsx:547 +#: src/view/shell/Drawer.tsx:72 +#: src/view/shell/Drawer.tsx:549 +#: src/view/shell/Drawer.tsx:550 msgid "Profile" msgstr "Profil" #: src/view/com/modals/EditProfile.tsx:128 msgid "Profile updated" -msgstr "" +msgstr "Profil mis à jour" #: src/view/screens/Settings.tsx:882 msgid "Protect your account by verifying your email." @@ -2598,7 +2615,7 @@ msgstr "Protégez votre compte en vérifiant votre e-mail." #: src/view/screens/ModerationModlists.tsx:61 msgid "Public, shareable lists of users to mute or block in bulk." -msgstr "Listes publiques et partageables d’utilisateurs à masquer ou à bloquer." +msgstr "Listes publiques et partageables de comptes à masquer ou à bloquer." #: src/view/screens/Lists.tsx:61 msgid "Public, shareable lists which can drive feeds." @@ -2606,16 +2623,16 @@ msgstr "Les listes publiques et partageables qui peuvent alimenter les fils d’ #: src/view/com/composer/Composer.tsx:335 msgid "Publish post" -msgstr "" +msgstr "Publier le post" #: src/view/com/composer/Composer.tsx:335 msgid "Publish reply" -msgstr "" +msgstr "Publier la réponse" #: src/view/com/modals/Repost.tsx:65 msgctxt "action" msgid "Quote post" -msgstr "" +msgstr "Citer le post" #: src/view/com/util/post-ctrls/RepostButton.web.tsx:58 msgid "Quote post" @@ -2624,15 +2641,11 @@ msgstr "Citer le post" #: src/view/com/modals/Repost.tsx:70 msgctxt "action" msgid "Quote Post" -msgstr "" - -#: src/view/com/modals/Repost.tsx:56 -#~ msgid "Quote Post" -#~ msgstr "Citer un post" +msgstr "Citer le post" #: src/view/screens/PreferencesThreads.tsx:86 msgid "Random (aka \"Poster's Roulette\")" -msgstr "" +msgstr "Aléatoire" #: src/view/com/modals/EditImage.tsx:236 msgid "Ratios" @@ -2644,7 +2657,7 @@ msgstr "Fils d’actu recommandés" #: src/view/com/auth/onboarding/RecommendedFollows.tsx:180 msgid "Recommended Users" -msgstr "Utilisateurs recommandés" +msgstr "Comptes recommandés" #: src/view/com/modals/ListAddRemoveUsers.tsx:264 #: src/view/com/modals/SelfLabel.tsx:83 @@ -2656,7 +2669,7 @@ msgstr "Supprimer" #: src/view/com/feeds/FeedSourceCard.tsx:106 msgid "Remove {0} from my feeds?" -msgstr "Supprimer {0} de mes fils d’actu ?" +msgstr "Supprimer {0} de mes fils d’actu ?" #: src/view/com/util/AccountDropdownBtn.tsx:22 msgid "Remove account" @@ -2685,15 +2698,15 @@ msgstr "Supprimer l’aperçu d’image" #: src/view/com/modals/Repost.tsx:47 msgid "Remove repost" -msgstr "" +msgstr "Supprimer le repost" #: src/view/com/feeds/FeedSourceCard.tsx:173 msgid "Remove this feed from my feeds?" -msgstr "Supprimer ce fil d’actu ?" +msgstr "Supprimer ce fil d’actu ?" #: src/view/com/posts/FeedErrorMessage.tsx:132 msgid "Remove this feed from your saved feeds?" -msgstr "Supprimer ce fil d’actu de vos fils d’actu enregistrés ?" +msgstr "Supprimer ce fil d’actu de vos fils d’actu enregistrés ?" #: src/view/com/modals/ListAddRemoveUsers.tsx:199 #: src/view/com/modals/UserAddRemoveLists.tsx:136 @@ -2703,11 +2716,11 @@ msgstr "Supprimé de la liste" #: src/view/com/feeds/FeedSourceCard.tsx:111 #: src/view/com/feeds/FeedSourceCard.tsx:178 msgid "Removed from my feeds" -msgstr "" +msgstr "Supprimé de mes fils d’actu" #: src/view/com/composer/ExternalEmbed.tsx:71 msgid "Removes default thumbnail from {0}" -msgstr "" +msgstr "Supprime la miniature par défaut de {0}" #: src/view/screens/Profile.tsx:162 msgid "Replies" @@ -2720,7 +2733,7 @@ msgstr "Les réponses à ce fil de discussion sont désactivées" #: src/view/com/composer/Composer.tsx:348 msgctxt "action" msgid "Reply" -msgstr "" +msgstr "Répondre" #: src/view/screens/PreferencesHomeFeed.tsx:144 msgid "Reply Filters" @@ -2730,7 +2743,7 @@ msgstr "Filtres de réponse" #: src/view/com/posts/FeedItem.tsx:286 msgctxt "description" msgid "Reply to <0/>" -msgstr "" +msgstr "Réponse à <0/>" #: src/view/com/modals/report/Modal.tsx:166 msgid "Report {collectionName}" @@ -2759,7 +2772,7 @@ msgstr "Signaler le post" #: src/view/com/util/post-ctrls/RepostButton.tsx:61 msgctxt "action" msgid "Repost" -msgstr "" +msgstr "Republier" #: src/view/com/util/post-ctrls/RepostButton.web.tsx:48 msgid "Repost" @@ -2776,32 +2789,36 @@ msgstr "Republié par" #: src/view/com/posts/FeedItem.tsx:206 msgid "Reposted by {0})" -msgstr "" +msgstr "Republié par {0})" #: src/view/com/posts/FeedItem.tsx:223 msgid "Reposted by <0/>" -msgstr "" +msgstr "Republié par <0/>" #: src/view/com/notifications/FeedItem.tsx:162 msgid "reposted your post" -msgstr "" +msgstr "a republié votre post" #: src/view/com/post-thread/PostThreadItem.tsx:189 msgid "Reposts of this post" -msgstr "" +msgstr "Reposts de ce post" #: src/view/com/modals/ChangeEmail.tsx:181 #: src/view/com/modals/ChangeEmail.tsx:183 msgid "Request Change" msgstr "Demande de modification" +#: src/view/com/auth/create/Step2.tsx:115 +msgid "Request code" +msgstr "" + #: src/view/screens/Settings.tsx:450 msgid "Require alt text before posting" msgstr "Nécessiter un texte alt avant de publier" -#: src/view/com/auth/create/Step2.tsx:68 +#: src/view/com/auth/create/Step1.tsx:97 msgid "Required for this provider" -msgstr "Obligatoire pour ce fournisseur" +msgstr "Obligatoire pour cet hébergeur" #: src/view/com/auth/login/SetNewPasswordForm.tsx:98 #: src/view/com/auth/login/SetNewPasswordForm.tsx:108 @@ -2810,11 +2827,11 @@ msgstr "Réinitialiser le code" #: src/view/screens/Settings.tsx:757 msgid "Reset onboarding" -msgstr "" +msgstr "Réinitialiser le didacticiel" #: src/view/screens/Settings.tsx:760 msgid "Reset onboarding state" -msgstr "Réinitialisation de l’état d’accueil" +msgstr "Réinitialisation du didacticiel" #: src/view/com/auth/login/ForgotPasswordForm.tsx:100 msgid "Reset password" @@ -2822,7 +2839,7 @@ msgstr "Réinitialiser mot de passe" #: src/view/screens/Settings.tsx:747 msgid "Reset preferences" -msgstr "" +msgstr "Réinitialiser les préférences" #: src/view/screens/Settings.tsx:750 msgid "Reset preferences state" @@ -2838,15 +2855,16 @@ msgstr "Réinitialise l’état des préférences" #: src/view/com/auth/login/LoginForm.tsx:266 msgid "Retries login" -msgstr "" +msgstr "Réessaye la connection" #: src/view/com/util/error/ErrorMessage.tsx:57 #: src/view/com/util/error/ErrorScreen.tsx:67 msgid "Retries the last action, which errored out" -msgstr "" +msgstr "Réessaye la dernière action, qui a échoué" -#: src/view/com/auth/create/CreateAccount.tsx:163 -#: src/view/com/auth/create/CreateAccount.tsx:167 +#: src/view/com/auth/create/CreateAccount.tsx:164 +#: src/view/com/auth/create/CreateAccount.tsx:168 +#: src/view/com/auth/create/Step2.tsx:150 #: src/view/com/auth/login/LoginForm.tsx:265 #: src/view/com/auth/login/LoginForm.tsx:268 #: src/view/com/util/error/ErrorMessage.tsx:55 @@ -2854,19 +2872,23 @@ msgstr "" msgid "Retry" msgstr "Réessayer" +#: src/view/com/auth/create/Step2.tsx:143 +msgid "Retry." +msgstr "" + #: src/view/screens/ProfileList.tsx:877 msgid "Return to previous page" -msgstr "" +msgstr "Retourne à la page précédente" #: src/view/shell/desktop/RightNav.tsx:59 msgid "SANDBOX. Posts and accounts are not permanent." -msgstr "" +msgstr "SANDBOX. Les posts et les comptes ne sont pas permanents." #: src/view/com/lightbox/Lightbox.tsx:125 #: src/view/com/modals/CreateOrEditList.tsx:278 msgctxt "action" msgid "Save" -msgstr "" +msgstr "Enregistrer" #: src/view/com/modals/BirthDateSettings.tsx:94 #: src/view/com/modals/BirthDateSettings.tsx:97 @@ -2899,15 +2921,15 @@ msgstr "Fils d’actu enregistrés" #: src/view/com/modals/EditProfile.tsx:225 msgid "Saves any changes to your profile" -msgstr "" +msgstr "Enregistre toutes les modifications apportées à votre profil" #: src/view/com/modals/ChangeHandle.tsx:171 msgid "Saves handle change to {handle}" -msgstr "" +msgstr "Enregistre le changement de pseudo en {handle}" #: src/view/screens/ProfileList.tsx:833 msgid "Scroll to top" -msgstr "" +msgstr "Remonter en haut" #: src/Navigation.tsx:437 #: src/view/com/auth/LoggedOut.tsx:122 @@ -2921,8 +2943,8 @@ msgstr "" #: src/view/shell/desktop/LeftNav.tsx:324 #: src/view/shell/desktop/Search.tsx:161 #: src/view/shell/desktop/Search.tsx:170 -#: src/view/shell/Drawer.tsx:362 -#: src/view/shell/Drawer.tsx:363 +#: src/view/shell/Drawer.tsx:365 +#: src/view/shell/Drawer.tsx:366 msgid "Search" msgstr "Recherche" @@ -2930,7 +2952,7 @@ msgstr "Recherche" #: src/view/com/auth/LoggedOut.tsx:105 #: src/view/com/modals/ListAddRemoveUsers.tsx:70 msgid "Search for users" -msgstr "Rechercher des utilisateurs" +msgstr "Rechercher des comptes" #: src/view/com/modals/ChangeEmail.tsx:110 msgid "Security Step Required" @@ -2938,7 +2960,7 @@ msgstr "Étape de sécurité requise" #: src/view/screens/SavedFeeds.tsx:163 msgid "See this guide" -msgstr "" +msgstr "Voir ce guide" #: src/view/com/auth/HomeLoggedOutCTA.tsx:39 msgid "See what's next" @@ -2946,7 +2968,7 @@ msgstr "Voir la suite" #: src/view/com/util/Selector.tsx:106 msgid "Select {item}" -msgstr "" +msgstr "Sélectionner {item}" #: src/view/com/modals/ServerInput.tsx:75 msgid "Select Bluesky Social" @@ -2958,15 +2980,16 @@ msgstr "Sélectionner un compte existant" #: src/view/com/util/Selector.tsx:107 msgid "Select option {i} of {numItems}" -msgstr "" +msgstr "Sélectionne l’option {i} sur {numItems}" +#: src/view/com/auth/create/Step1.tsx:77 #: src/view/com/auth/login/LoginForm.tsx:147 msgid "Select service" msgstr "Sélectionner un service" #: src/view/screens/LanguageSettings.tsx:281 msgid "Select which languages you want your subscribed feeds to include. If none are selected, all languages will be shown." -msgstr "Sélectionnez les langues que vous souhaitez voir figurer dans les fils d’actu auxquels vous êtes abonné. Si aucune langue n’est sélectionnée, toutes les langues seront affichées." +msgstr "Sélectionnez les langues que vous souhaitez voir figurer dans les fils d’actu que vous suivez. Si aucune langue n’est sélectionnée, toutes les langues seront affichées." #: src/view/screens/LanguageSettings.tsx:98 msgid "Select your app language for the default text to display in the app" @@ -2988,14 +3011,10 @@ msgstr "Envoyer e-mail" #: src/view/com/modals/DeleteAccount.tsx:140 msgctxt "action" msgid "Send Email" -msgstr "" +msgstr "Envoyer l’e-mail" -#: src/view/com/modals/DeleteAccount.tsx:138 -#~ msgid "Send Email" -#~ msgstr "Envoyer e-mail" - -#: src/view/shell/Drawer.tsx:295 -#: src/view/shell/Drawer.tsx:316 +#: src/view/shell/Drawer.tsx:298 +#: src/view/shell/Drawer.tsx:319 msgid "Send feedback" msgstr "Envoyer des commentaires" @@ -3005,83 +3024,84 @@ msgstr "Envoyer le rapport" #: src/view/com/modals/DeleteAccount.tsx:129 msgid "Sends email with confirmation code for account deletion" -msgstr "" +msgstr "Envoie un e-mail avec le code de confirmation pour la suppression du compte" #: src/view/com/modals/ContentFilteringSettings.tsx:306 msgid "Set {value} for {labelGroup} content moderation policy" -msgstr "" +msgstr "Choisis {value} pour la politique de modération de contenu {labelGroup}" #: src/view/com/modals/ContentFilteringSettings.tsx:155 #: src/view/com/modals/ContentFilteringSettings.tsx:174 msgctxt "action" msgid "Set Age" -msgstr "" +msgstr "Enregistrer l’âge" #: src/view/screens/Settings.tsx:482 msgid "Set color theme to dark" -msgstr "" +msgstr "Change le thème de couleur en sombre" #: src/view/screens/Settings.tsx:475 msgid "Set color theme to light" -msgstr "" +msgstr "Change le thème de couleur en clair" #: src/view/screens/Settings.tsx:469 msgid "Set color theme to system setting" -msgstr "" +msgstr "Change le thème de couleur en fonction du paramètre système" #: src/view/com/auth/login/SetNewPasswordForm.tsx:78 msgid "Set new password" msgstr "Définir un nouveau mot de passe" -#: src/view/com/auth/create/Step2.tsx:133 +#: src/view/com/auth/create/Step1.tsx:169 msgid "Set password" -msgstr "" +msgstr "Définit le mot de passe" #: src/view/screens/PreferencesHomeFeed.tsx:225 msgid "Set this setting to \"No\" to hide all quote posts from your feed. Reposts will still be visible." -msgstr "Choisissez « Non » pour cacher toutes les citations sur votre fils d’actu. Les reposts seront toujours visibles." +msgstr "Choisissez « Non » pour cacher toutes les citations sur votre fils d’actu. Les reposts seront toujours visibles." #: src/view/screens/PreferencesHomeFeed.tsx:122 msgid "Set this setting to \"No\" to hide all replies from your feed." -msgstr "Choisissez « Non » pour cacher toutes les réponses dans votre fils d’actu." +msgstr "Choisissez « Non » pour cacher toutes les réponses dans votre fils d’actu." #: src/view/screens/PreferencesHomeFeed.tsx:191 msgid "Set this setting to \"No\" to hide all reposts from your feed." -msgstr "Choisissez « Non » pour cacher toutes les reposts de votre fils d’actu." +msgstr "Choisissez « Non » pour cacher toutes les reposts de votre fils d’actu." #: src/view/screens/PreferencesThreads.tsx:122 msgid "Set this setting to \"Yes\" to show replies in a threaded view. This is an experimental feature." -msgstr "Choisissez « Oui » pour afficher les réponses dans un fil de discussion. C’est une fonctionnalité expérimentale." +msgstr "Choisissez « Oui » pour afficher les réponses dans un fil de discussion. C’est une fonctionnalité expérimentale." #: src/view/screens/PreferencesHomeFeed.tsx:261 msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your following feed. This is an experimental feature." -msgstr "Choisissez « Oui » pour afficher des échantillons de vos fils d’actu enregistrés dans votre fils d’actu suivant. C’est une fonctionnalité expérimentale." +msgstr "Choisissez « Oui » pour afficher des échantillons de vos fils d’actu enregistrés dans votre fils d’actu suivant. C’est une fonctionnalité expérimentale." #: src/view/com/modals/ChangeHandle.tsx:266 msgid "Sets Bluesky username" -msgstr "" +msgstr "Définit le pseudo Bluesky" #: src/view/com/auth/login/ForgotPasswordForm.tsx:153 msgid "Sets email for password reset" -msgstr "" +msgstr "Définit l’e-mail pour la réinitialisation du mot de passe" #: src/view/com/auth/login/ForgotPasswordForm.tsx:118 msgid "Sets hosting provider for password reset" -msgstr "" +msgstr "Définit l’hébergeur pour la réinitialisation du mot de passe" #: src/view/com/auth/create/Step1.tsx:143 -msgid "Sets hosting provider to {label}" -msgstr "" +#~ msgid "Sets hosting provider to {label}" +#~ msgstr "Définit l’hébergeur à {label}" +#: src/view/com/auth/create/Step1.tsx:78 #: src/view/com/auth/login/LoginForm.tsx:148 msgid "Sets server for the Bluesky client" -msgstr "" +msgstr "Définit le serveur pour le client Bluesky" #: src/Navigation.tsx:136 #: src/view/screens/Settings.tsx:294 #: src/view/shell/desktop/LeftNav.tsx:433 -#: src/view/shell/Drawer.tsx:567 -#: src/view/shell/Drawer.tsx:568 +#: src/view/shell/Drawer.tsx:570 +#: src/view/shell/Drawer.tsx:571 msgid "Settings" msgstr "Paramètres" @@ -3092,7 +3112,7 @@ msgstr "Activité sexuelle ou nudité érotique." #: src/view/com/lightbox/Lightbox.tsx:134 msgctxt "action" msgid "Share" -msgstr "" +msgstr "Partager" #: src/view/com/profile/ProfileHeader.tsx:342 #: src/view/com/util/forms/PostDropdownBtn.tsx:151 @@ -3113,7 +3133,7 @@ msgstr "Afficher" #: src/view/screens/PreferencesHomeFeed.tsx:68 msgid "Show all replies" -msgstr "" +msgstr "Afficher toutes les réponses" #: src/view/com/util/moderation/ScreenHider.tsx:132 msgid "Show anyway" @@ -3121,17 +3141,17 @@ msgstr "Afficher quand même" #: src/view/com/modals/EmbedConsent.tsx:87 msgid "Show embeds from {0}" -msgstr "" +msgstr "Afficher les intégrations de {0}" #: src/view/com/profile/ProfileHeader.tsx:498 msgid "Show follows similar to {0}" -msgstr "" +msgstr "Afficher les suivis similaires à {0}" #: src/view/com/post-thread/PostThreadItem.tsx:569 #: src/view/com/post/Post.tsx:196 #: src/view/com/posts/FeedItem.tsx:362 msgid "Show More" -msgstr "" +msgstr "Voir plus" #: src/view/screens/PreferencesHomeFeed.tsx:258 msgid "Show Posts from My Feeds" @@ -3139,7 +3159,7 @@ msgstr "Afficher les posts de mes fils d’actu" #: src/view/screens/PreferencesHomeFeed.tsx:222 msgid "Show Quote Posts" -msgstr "Afficher les posts de citation" +msgstr "Afficher les citations" #: src/view/screens/PreferencesHomeFeed.tsx:119 msgid "Show Replies" @@ -3151,7 +3171,7 @@ msgstr "Afficher les réponses des personnes que vous suivez avant toutes les au #: src/view/screens/PreferencesHomeFeed.tsx:70 msgid "Show replies with at least {value} {0}" -msgstr "" +msgstr "Afficher les réponses avec au moins {value} {0}" #: src/view/screens/PreferencesHomeFeed.tsx:188 msgid "Show Reposts" @@ -3160,19 +3180,19 @@ msgstr "Afficher les reposts" #: src/view/com/util/moderation/ContentHider.tsx:67 #: src/view/com/util/moderation/PostHider.tsx:61 msgid "Show the content" -msgstr "" +msgstr "Afficher le contenu" #: src/view/com/notifications/FeedItem.tsx:350 msgid "Show users" -msgstr "Afficher les utilisateurs" +msgstr "Afficher les comptes" #: src/view/com/profile/ProfileHeader.tsx:501 msgid "Shows a list of users similar to this user." -msgstr "" +msgstr "Affiche une liste de comptes similaires à ce compte." #: src/view/com/profile/ProfileHeader.tsx:545 msgid "Shows posts from {0} in your feed" -msgstr "" +msgstr "Affiche les posts de {0} dans votre fil d’actu" #: src/view/com/auth/HomeLoggedOutCTA.tsx:70 #: src/view/com/auth/login/Login.tsx:98 @@ -3240,27 +3260,31 @@ msgstr "Connecté en tant que" #: src/view/com/auth/login/ChooseAccountForm.tsx:103 msgid "Signed in as @{0}" -msgstr "" +msgstr "Connecté en tant que @{0}" #: src/view/com/modals/SwitchAccount.tsx:66 msgid "Signs {0} out of Bluesky" -msgstr "" +msgstr "Déconnecte {0} de Bluesky" #: src/view/com/auth/onboarding/WelcomeMobile.tsx:33 msgid "Skip" msgstr "Ignorer" +#: src/view/com/auth/create/Step2.tsx:70 +msgid "SMS verification" +msgstr "" + #: src/view/com/modals/ProfilePreview.tsx:62 msgid "Something went wrong and we're not sure what." -msgstr "" +msgstr "Quelque chose n’a pas marché, mais on ne sait pas trop quoi." #: src/view/com/modals/Waitlist.tsx:51 msgid "Something went wrong. Check your email and try again." -msgstr "" +msgstr "Quelque chose n’a pas marché. Vérifiez vos e-mails et réessayez." -#: src/App.native.tsx:57 +#: src/App.native.tsx:62 msgid "Sorry! Your session expired. Please log in again." -msgstr "" +msgstr "Désolé ! Votre session a expiré. Essayez de vous reconnecter." #: src/view/screens/PreferencesThreads.tsx:69 msgid "Sort Replies" @@ -3268,29 +3292,33 @@ msgstr "Trier les réponses" #: src/view/screens/PreferencesThreads.tsx:72 msgid "Sort replies to the same post by:" -msgstr "Trier les réponses au même post par :" +msgstr "Trier les réponses au même post par :" #: src/view/com/modals/crop-image/CropImage.web.tsx:122 msgid "Square" msgstr "Carré" -#: src/view/com/auth/create/Step1.tsx:90 #: src/view/com/modals/ServerInput.tsx:62 msgid "Staging" -msgstr "Mise en scène" +msgstr "Serveur de test" #: src/view/screens/Settings.tsx:804 msgid "Status page" -msgstr "Page de statut" +msgstr "État du service" + +#: src/view/com/auth/create/StepHeader.tsx:22 +msgid "Step {0} of {numSteps}" +msgstr "" #: src/view/com/auth/create/StepHeader.tsx:15 -msgid "Step {step} of 3" -msgstr "" +#~ msgid "Step {step} of 3" +#~ msgstr "Étape {step} sur 3" #: src/view/screens/Settings.tsx:276 msgid "Storage cleared, you need to restart the app now." -msgstr "" +msgstr "Stockage effacé, vous devez redémarrer l’application maintenant." +#: src/Navigation.tsx:204 #: src/view/screens/Settings.tsx:740 msgid "Storybook" msgstr "Historique" @@ -3308,8 +3336,8 @@ msgid "Subscribe to this list" msgstr "S’abonner à cette liste" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "Abonné·e" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3317,11 +3345,11 @@ msgstr "Suivis suggérés" #: src/view/com/profile/ProfileHeaderSuggestedFollows.tsx:64 msgid "Suggested for you" -msgstr "" +msgstr "Suggérés pour vous" #: src/view/com/modals/SelfLabel.tsx:95 msgid "Suggestive" -msgstr "" +msgstr "Suggestif" #: src/Navigation.tsx:214 #: src/view/screens/Support.tsx:30 @@ -3331,7 +3359,7 @@ msgstr "Soutien" #: src/view/com/modals/ProfilePreview.tsx:110 msgid "Swipe up to see more" -msgstr "" +msgstr "Glisser vers le haut pour en voir plus" #: src/view/com/modals/SwitchAccount.tsx:117 msgid "Switch Account" @@ -3340,16 +3368,16 @@ msgstr "Changer de compte" #: src/view/com/modals/SwitchAccount.tsx:97 #: src/view/screens/Settings.tsx:137 msgid "Switch to {0}" -msgstr "" +msgstr "Basculer sur {0}" #: src/view/com/modals/SwitchAccount.tsx:98 #: src/view/screens/Settings.tsx:138 msgid "Switches the account you are logged in to" -msgstr "" +msgstr "Bascule le compte auquel vous êtes connectés vers" #: src/view/screens/Settings.tsx:466 msgid "System" -msgstr "" +msgstr "Système" #: src/view/screens/Settings.tsx:720 msgid "System log" @@ -3361,7 +3389,7 @@ msgstr "Grand" #: src/view/com/util/images/AutoSizedImage.tsx:70 msgid "Tap to view fully" -msgstr "" +msgstr "Tapper pour voir en entier" #: src/view/shell/desktop/RightNav.tsx:93 msgid "Terms" @@ -3370,7 +3398,7 @@ msgstr "Conditions générales" #: src/Navigation.tsx:224 #: src/view/screens/Settings.tsx:818 #: src/view/screens/TermsOfService.tsx:29 -#: src/view/shell/Drawer.tsx:256 +#: src/view/shell/Drawer.tsx:259 msgid "Terms of Service" msgstr "Conditions d’utilisation" @@ -3401,11 +3429,7 @@ msgstr "Notre politique de confidentialité a été déplacée vers <0/>" #: src/view/screens/Support.tsx:36 msgid "The support form has been moved. If you need help, please <0/> or visit {HELP_DESK_URL} to get in touch with us." -msgstr "" - -#: src/view/screens/Support.tsx:36 -#~ msgid "The support form has been moved. If you need help, please<0/> or visit {HELP_DESK_URL} to get in touch with us." -#~ msgstr "Le formulaire d’assistance a été déplacé. Si vous avez besoin d’aide, veuillez <0/> ou rendez-vous sur {HELP_DESK_URL} pour nous contacter." +msgstr "Le formulaire d’assistance a été déplacé. Si vous avez besoin d’aide, veuillez <0/> ou rendez-vous sur {HELP_DESK_URL} pour nous contacter." #: src/view/screens/TermsOfService.tsx:33 msgid "The Terms of Service have been moved to" @@ -3413,15 +3437,15 @@ msgstr "Nos conditions d’utilisation ont été déplacées vers" #: src/view/screens/ProfileFeed.tsx:558 msgid "There was an an issue contacting the server, please check your internet connection and try again." -msgstr "" +msgstr "Il y a eu un problème de connexion au serveur, veuillez vérifier votre connexion Internet et réessayez." #: src/view/com/posts/FeedErrorMessage.tsx:139 msgid "There was an an issue removing this feed. Please check your internet connection and try again." -msgstr "" +msgstr "Il y a eu un problème lors de la suppression du fil, veuillez vérifier votre connexion Internet et réessayez." #: src/view/screens/ProfileFeed.tsx:218 msgid "There was an an issue updating your feeds, please check your internet connection and try again." -msgstr "" +msgstr "Il y a eu un problème lors de la mise à jour de vos fils d’actu, veuillez vérifier votre connexion Internet et réessayez." #: src/view/screens/ProfileFeed.tsx:245 #: src/view/screens/ProfileList.tsx:266 @@ -3429,7 +3453,7 @@ msgstr "" #: src/view/screens/SavedFeeds.tsx:231 #: src/view/screens/SavedFeeds.tsx:252 msgid "There was an issue contacting the server" -msgstr "" +msgstr "Il y a eu un problème de connexion au serveur" #: src/view/com/auth/onboarding/RecommendedFeedsItem.tsx:57 #: src/view/com/auth/onboarding/RecommendedFeedsItem.tsx:66 @@ -3437,32 +3461,32 @@ msgstr "" #: src/view/com/feeds/FeedSourceCard.tsx:127 #: src/view/com/feeds/FeedSourceCard.tsx:181 msgid "There was an issue contacting your server" -msgstr "" +msgstr "Il y a eu un problème de connexion à votre serveur" #: src/view/com/notifications/Feed.tsx:115 msgid "There was an issue fetching notifications. Tap here to try again." -msgstr "" +msgstr "Il y a eu un problème lors de la récupération des notifications. Appuyez ici pour réessayer." -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." -msgstr "" +msgstr "Il y a eu un problème lors de la récupération des posts. Appuyez ici pour réessayer." #: src/view/com/lists/ListMembers.tsx:172 msgid "There was an issue fetching the list. Tap here to try again." -msgstr "" +msgstr "Il y a eu un problème lors de la récupération de la liste. Appuyez ici pour réessayer." #: src/view/com/feeds/ProfileFeedgens.tsx:148 #: src/view/com/lists/ProfileLists.tsx:155 msgid "There was an issue fetching your lists. Tap here to try again." -msgstr "" +msgstr "Il y a eu un problème lors de la récupération de vos listes. Appuyez ici pour réessayer." #: src/view/com/modals/ContentFilteringSettings.tsx:126 msgid "There was an issue syncing your preferences with the server" -msgstr "" +msgstr "Il y a eu un problème de synchronisation de vos préférences avec le serveur" #: src/view/screens/AppPasswords.tsx:66 msgid "There was an issue with fetching your app passwords" -msgstr "" +msgstr "Il y a eu un problème lors de la récupération de vos mots de passe d’application" #: src/view/com/profile/ProfileHeader.tsx:204 #: src/view/com/profile/ProfileHeader.tsx:225 @@ -3471,37 +3495,38 @@ msgstr "" #: src/view/com/profile/ProfileHeader.tsx:297 #: src/view/com/profile/ProfileHeader.tsx:319 msgid "There was an issue! {0}" -msgstr "" +msgstr "Il y a eu un problème ! {0}" #: src/view/screens/ProfileList.tsx:287 #: src/view/screens/ProfileList.tsx:306 #: src/view/screens/ProfileList.tsx:328 #: src/view/screens/ProfileList.tsx:347 msgid "There was an issue. Please check your internet connection and try again." -msgstr "" +msgstr "Il y a eu un problème. Veuillez vérifier votre connexion Internet et réessayez." #: src/view/com/util/ErrorBoundary.tsx:35 msgid "There was an unexpected issue in the application. Please let us know if this happened to you!" -msgstr "Un problème inattendu s’est produit dans l’application. N’hésitez pas à nous faire savoir si cela vous est arrivé !" +msgstr "Un problème inattendu s’est produit dans l’application. N’hésitez pas à nous faire savoir si cela vous est arrivé !" -#~ msgid "This {0} has been labeled." -#~ msgstr "Ce {0} a été classé." +#: src/view/com/auth/create/Step2.tsx:47 +msgid "There's something wrong with this number. Please include your country and/or area code!" +msgstr "" #: src/view/com/util/moderation/ScreenHider.tsx:88 msgid "This {screenDescription} has been flagged:" -msgstr "Ce {screenDescription} a été signalé :" +msgstr "Ce {screenDescription} a été signalé :" #: src/view/com/util/moderation/ScreenHider.tsx:83 msgid "This account has requested that users sign in to view their profile." -msgstr "Ce compte a demandé aux utilisateurs de s’identifier pour voir son profil." +msgstr "Ce compte a demandé aux personnes de se connecter pour voir son profil." #: src/view/com/modals/EmbedConsent.tsx:68 msgid "This content is hosted by {0}. Do you want to enable external media?" -msgstr "" +msgstr "Ce contenu est hébergé par {0}. Voulez-vous activer les médias externes ?" #: src/view/com/modals/ModerationDetails.tsx:67 msgid "This content is not available because one of the users involved has blocked the other." -msgstr "" +msgstr "Ce contenu n’est pas disponible car l’un des comptes impliqués a bloqué l’autre." #: src/view/com/posts/FeedErrorMessage.tsx:108 msgid "This content is not viewable without a Bluesky account." @@ -3515,35 +3540,35 @@ msgstr "Ce fil d’actu reçoit actuellement un trafic important, il est tempora #: src/view/screens/ProfileFeed.tsx:484 #: src/view/screens/ProfileList.tsx:639 msgid "This feed is empty!" -msgstr "" +msgstr "Ce fil d’actu est vide !" #: src/view/com/posts/CustomFeedEmptyState.tsx:37 msgid "This feed is empty! You may need to follow more users or tune your language settings." -msgstr "" +msgstr "Ce fil d’actu est vide ! Vous devriez peut-être suivre plus de comptes ou ajuster vos paramètres de langue." #: src/view/com/modals/BirthDateSettings.tsx:61 msgid "This information is not shared with other users." -msgstr "Ces informations ne sont pas partagées avec d’autres utilisateurs." +msgstr "Ces informations ne sont pas partagées avec d’autres personnes." #: src/view/com/modals/VerifyEmail.tsx:119 msgid "This is important in case you ever need to change your email or reset your password." msgstr "Ceci est important au cas où vous auriez besoin de changer d’e-mail ou de réinitialiser votre mot de passe." #: src/view/com/auth/create/Step1.tsx:55 -msgid "This is the service that keeps you online." -msgstr "C’est le service qui vous permet de rester en ligne." +#~ msgid "This is the service that keeps you online." +#~ msgstr "C’est le service qui vous permet de rester en ligne." #: src/view/com/modals/LinkWarning.tsx:58 msgid "This link is taking you to the following website:" -msgstr "Ce lien vous conduit au site Web suivant :" +msgstr "Ce lien vous conduit au site Web suivant :" #: src/view/screens/ProfileList.tsx:813 msgid "This list is empty!" -msgstr "" +msgstr "Cette liste est vide !" #: src/view/com/modals/AddAppPasswords.tsx:105 msgid "This name is already in use" -msgstr "" +msgstr "Ce nom est déjà utilisé" #: src/view/com/post-thread/PostThreadItem.tsx:123 msgid "This post has been deleted." @@ -3551,15 +3576,15 @@ msgstr "Ce post a été supprimé." #: src/view/com/modals/ModerationDetails.tsx:62 msgid "This user has blocked you. You cannot view their content." -msgstr "" +msgstr "Ce compte vous a bloqué. Vous ne pouvez pas voir son contenu." #: src/view/com/modals/ModerationDetails.tsx:42 msgid "This user is included in the <0/> list which you have blocked." -msgstr "" +msgstr "Ce compte est inclus dans la liste <0/> que vous avez bloquée." #: src/view/com/modals/ModerationDetails.tsx:74 msgid "This user is included the <0/> list which you have muted." -msgstr "" +msgstr "Ce compte est inclus dans la liste <0/> que vous avez masquée." #: src/view/com/modals/SelfLabel.tsx:137 msgid "This warning is only available for posts with media attached." @@ -3580,7 +3605,7 @@ msgstr "Mode arborescent" #: src/Navigation.tsx:254 msgid "Threads Preferences" -msgstr "" +msgstr "Préférences de fils de discussion" #: src/view/com/util/forms/DropdownButton.tsx:234 msgid "Toggle dropdown" @@ -3599,11 +3624,7 @@ msgstr "Traduire" #: src/view/com/util/error/ErrorScreen.tsx:75 msgctxt "action" msgid "Try again" -msgstr "" - -#: src/view/com/util/error/ErrorScreen.tsx:73 -#~ msgid "Try again" -#~ msgstr "Réessayer" +msgstr "Réessayer" #: src/view/screens/ProfileList.tsx:484 msgid "Un-block list" @@ -3613,7 +3634,7 @@ msgstr "Débloquer la liste" msgid "Un-mute list" msgstr "Réafficher cette liste" -#: src/view/com/auth/create/CreateAccount.tsx:65 +#: src/view/com/auth/create/CreateAccount.tsx:66 #: src/view/com/auth/login/ForgotPasswordForm.tsx:87 #: src/view/com/auth/login/Login.tsx:76 #: src/view/com/auth/login/LoginForm.tsx:120 @@ -3623,7 +3644,7 @@ msgstr "Impossible de contacter votre service. Veuillez vérifier votre connexio #: src/view/com/profile/ProfileHeader.tsx:475 msgctxt "action" msgid "Unblock" -msgstr "" +msgstr "Débloquer" #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 @@ -3645,23 +3666,23 @@ msgstr "Annuler le repost" #: src/view/com/profile/FollowButton.tsx:55 msgctxt "action" msgid "Unfollow" -msgstr "" +msgstr "Se désabonner" #: src/view/com/profile/ProfileHeader.tsx:524 msgid "Unfollow {0}" -msgstr "" +msgstr "Se désabonner de {0}" -#: src/view/com/auth/create/state.ts:216 +#: src/view/com/auth/create/state.ts:289 msgid "Unfortunately, you do not meet the requirements to create an account." msgstr "Malheureusement, vous ne remplissez pas les conditions requises pour créer un compte." #: src/view/com/util/post-ctrls/PostCtrls.tsx:189 msgid "Unlike" -msgstr "" +msgstr "Déliker" #: src/view/screens/ProfileList.tsx:575 msgid "Unmute" -msgstr "" +msgstr "Réafficher" #: src/view/com/profile/ProfileHeader.tsx:373 msgid "Unmute Account" @@ -3674,7 +3695,7 @@ msgstr "Réafficher ce fil de discussion" #: src/view/screens/ProfileFeed.tsx:362 #: src/view/screens/ProfileList.tsx:559 msgid "Unpin" -msgstr "" +msgstr "Désépingler" #: src/view/screens/ProfileList.tsx:452 msgid "Unpin moderation list" @@ -3682,7 +3703,7 @@ msgstr "Supprimer la liste de modération" #: src/view/screens/ProfileFeed.tsx:354 msgid "Unsave" -msgstr "" +msgstr "Supprimer" #: src/view/com/modals/UserAddRemoveLists.tsx:54 msgid "Update {displayName} in Lists" @@ -3698,11 +3719,11 @@ msgstr "Mise à jour…" #: src/view/com/modals/ChangeHandle.tsx:455 msgid "Upload a text file to:" -msgstr "Télécharger un fichier texte vers :" +msgstr "Envoyer un fichier texte vers :" #: src/view/screens/AppPasswords.tsx:195 msgid "Use app passwords to login to other Bluesky clients without giving full access to your account or password." -msgstr "Utiliser les mots de passe de l’appli pour se connecter à d’autres clients Bluesky sans donner un accès complet à votre compte ou à votre mot de passe." +msgstr "Utilisez les mots de passe de l’appli pour se connecter à d’autres clients Bluesky sans donner un accès complet à votre compte ou à votre mot de passe." #: src/view/com/modals/ChangeHandle.tsx:515 msgid "Use default provider" @@ -3724,71 +3745,75 @@ msgstr "Utilisez-le pour vous connecter à l’autre application avec votre iden #: src/view/com/modals/ServerInput.tsx:105 msgid "Use your domain as your Bluesky client service provider" -msgstr "" +msgstr "Utilise votre domaine comme votre fournisseur de client Bluesky" #: src/view/com/modals/InviteCodes.tsx:200 msgid "Used by:" -msgstr "Utilisé par :" +msgstr "Utilisé par :" #: src/view/com/modals/ModerationDetails.tsx:54 msgid "User Blocked" -msgstr "" +msgstr "Compte bloqué" #: src/view/com/modals/ModerationDetails.tsx:40 msgid "User Blocked by List" -msgstr "" +msgstr "Compte bloqué par liste" #: src/view/com/modals/ModerationDetails.tsx:60 msgid "User Blocks You" -msgstr "" +msgstr "Compte qui vous bloque" #: src/view/com/auth/create/Step3.tsx:38 msgid "User handle" -msgstr "Pseudo utilisateur" +msgstr "Pseudo" #: src/view/com/lists/ListCard.tsx:84 #: src/view/com/modals/UserAddRemoveLists.tsx:182 msgid "User list by {0}" -msgstr "" +msgstr "Liste de compte de {0}" #: src/view/screens/ProfileList.tsx:741 msgid "User list by <0/>" -msgstr "" +msgstr "Liste de compte par <0/>" #: src/view/com/lists/ListCard.tsx:82 #: src/view/com/modals/UserAddRemoveLists.tsx:180 #: src/view/screens/ProfileList.tsx:739 msgid "User list by you" -msgstr "" +msgstr "Liste de compte par vous" #: src/view/com/modals/CreateOrEditList.tsx:138 msgid "User list created" -msgstr "" +msgstr "Liste de compte créée" #: src/view/com/modals/CreateOrEditList.tsx:125 msgid "User list updated" -msgstr "" +msgstr "Liste de compte mise à jour" #: src/view/screens/Lists.tsx:58 msgid "User Lists" -msgstr "Listes d’utilisateurs" +msgstr "Listes de comptes" #: src/view/com/auth/login/LoginForm.tsx:174 #: src/view/com/auth/login/LoginForm.tsx:192 msgid "Username or email address" -msgstr "Nom d’utilisateur ou e-mail" +msgstr "Pseudo ou e-mail" #: src/view/screens/ProfileList.tsx:775 msgid "Users" -msgstr "Utilisateurs" +msgstr "Comptes" #: src/view/com/threadgate/WhoCanReply.tsx:143 msgid "users followed by <0/>" -msgstr "utilisateurs suivis par <0/>" +msgstr "comptes suivis par <0/>" #: src/view/com/modals/Threadgate.tsx:106 msgid "Users in \"{0}\"" -msgstr "Utilisateurs dans « {0} »" +msgstr "Comptes dans « {0} »" + +#: src/view/com/auth/create/Step2.tsx:139 +msgid "Verification code" +msgstr "" #: src/view/screens/Settings.tsx:843 msgid "Verify email" @@ -3809,11 +3834,11 @@ msgstr "Confirmer le nouvel e-mail" #: src/view/com/modals/VerifyEmail.tsx:103 msgid "Verify Your Email" -msgstr "" +msgstr "Vérifiez votre e-mail" #: src/view/com/profile/ProfileHeader.tsx:701 msgid "View {0}'s avatar" -msgstr "" +msgstr "Voir l’avatar de {0}" #: src/view/screens/Log.tsx:52 msgid "View debug entry" @@ -3821,11 +3846,11 @@ msgstr "Afficher l’entrée de débogage" #: src/view/com/posts/FeedSlice.tsx:103 msgid "View full thread" -msgstr "" +msgstr "Voir le fil de discussion entier" #: src/view/com/posts/FeedErrorMessage.tsx:172 msgid "View profile" -msgstr "" +msgstr "Voir le profil" #: src/view/com/profile/ProfileSubpageHeader.tsx:128 msgid "View the avatar" @@ -3837,19 +3862,23 @@ msgstr "Visiter le site" #: src/view/com/modals/ContentFilteringSettings.tsx:254 msgid "Warn" +msgstr "Avertir" + +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from <0/>." msgstr "" #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." -msgstr "" +msgstr "Nous examinerons votre appel rapidement." -#: src/view/com/auth/create/CreateAccount.tsx:122 +#: src/view/com/auth/create/CreateAccount.tsx:123 msgid "We're so excited to have you join us!" -msgstr "Nous sommes ravis de vous accueillir !" +msgstr "Nous sommes ravis de vous accueillir !" #: src/view/screens/ProfileList.tsx:83 msgid "We're sorry, but we were unable to resolve this list. If this persists, please contact the list creator, @{handleOrDid}." -msgstr "" +msgstr "Nous sommes désolés, mais nous n’avons pas pu charger cette liste. Si cela persiste, veuillez contacter l’origine de la liste, @{handleOrDid}." #: src/view/screens/Search/Search.tsx:243 msgid "We're sorry, but your search could not be completed. Please try again in a few minutes." @@ -3857,7 +3886,7 @@ msgstr "Nous sommes désolés, mais votre recherche a été annulée. Veuillez r #: src/view/screens/NotFound.tsx:48 msgid "We're sorry! We can't find the page you were looking for." -msgstr "Nous sommes désolés ! La page que vous recherchez est introuvable." +msgstr "Nous sommes désolés ! La page que vous recherchez est introuvable." #: src/view/com/auth/onboarding/WelcomeMobile.tsx:46 msgid "Welcome to <0>Bluesky" @@ -3865,25 +3894,25 @@ msgstr "Bienvenue sur <0>Bluesky" #: src/view/com/modals/report/Modal.tsx:169 msgid "What is the issue with this {collectionName}?" -msgstr "Quel est le problème avec cette {collectionName} ?" +msgstr "Quel est le problème avec cette {collectionName} ?" #: src/view/com/auth/SplashScreen.tsx:34 #: src/view/com/composer/Composer.tsx:279 msgid "What's up?" -msgstr "Quoi de neuf ?" +msgstr "Quoi de neuf ?" #: src/view/com/modals/lang-settings/PostLanguagesSettings.tsx:78 msgid "Which languages are used in this post?" -msgstr "Quelles sont les langues utilisées dans ce post ?" +msgstr "Quelles sont les langues utilisées dans ce post ?" #: src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx:77 msgid "Which languages would you like to see in your algorithmic feeds?" -msgstr "Quelles langues aimeriez-vous voir apparaître dans vos flux algorithmiques ?" +msgstr "Quelles langues aimeriez-vous voir apparaître dans vos flux algorithmiques ?" #: src/view/com/composer/threadgate/ThreadgateBtn.tsx:47 #: src/view/com/modals/Threadgate.tsx:66 msgid "Who can reply" -msgstr "Qui peut répondre ?" +msgstr "Qui peut répondre ?" #: src/view/com/modals/crop-image/CropImage.web.tsx:102 msgid "Wide" @@ -3898,6 +3927,10 @@ msgstr "Rédiger un post" msgid "Write your reply" msgstr "Rédigez votre réponse" +#: src/view/com/auth/create/Step2.tsx:158 +msgid "XXXXXX" +msgstr "" + #: src/view/com/composer/select-language/SuggestedLanguage.tsx:82 #: src/view/screens/PreferencesHomeFeed.tsx:129 #: src/view/screens/PreferencesHomeFeed.tsx:201 @@ -3911,11 +3944,11 @@ msgstr "Oui" #: src/view/com/posts/FollowingEmptyState.tsx:67 #: src/view/com/posts/FollowingEndOfFeed.tsx:68 msgid "You can also discover new Custom Feeds to follow." -msgstr "" +msgstr "Vous pouvez aussi découvrir de nouveaux fils d’actu personnalisés à suivre." #: src/view/com/auth/create/Step1.tsx:106 -msgid "You can change hosting providers at any time." -msgstr "Vous pouvez changer d’hébergeur à tout moment." +#~ msgid "You can change hosting providers at any time." +#~ msgstr "Vous pouvez changer d’hébergeur à tout moment." #: src/view/com/auth/login/Login.tsx:158 #: src/view/com/auth/login/PasswordUpdatedForm.tsx:31 @@ -3924,7 +3957,7 @@ msgstr "Vous pouvez maintenant vous connecter avec votre nouveau mot de passe." #: src/view/com/modals/InviteCodes.tsx:66 msgid "You don't have any invite codes yet! We'll send you some when you've been on Bluesky for a little longer." -msgstr "Vous n’avez encore aucun code d’invitation ! Nous vous en enverrons lorsque vous serez sur Bluesky depuis un peu plus longtemps." +msgstr "Vous n’avez encore aucun code d’invitation ! Nous vous en enverrons lorsque vous serez sur Bluesky depuis un peu plus longtemps." #: src/view/screens/SavedFeeds.tsx:102 msgid "You don't have any pinned feeds." @@ -3932,7 +3965,7 @@ msgstr "Vous n’avez encore aucun fil épinglé." #: src/view/screens/Feeds.tsx:387 msgid "You don't have any saved feeds!" -msgstr "Vous n’avez encore aucun fil enregistré !" +msgstr "Vous n’avez encore aucun fil enregistré !" #: src/view/screens/SavedFeeds.tsx:135 msgid "You don't have any saved feeds." @@ -3944,11 +3977,11 @@ msgstr "Vous avez bloqué cet auteur ou vous avez été bloqué par celui-ci." #: src/view/com/modals/ModerationDetails.tsx:56 msgid "You have blocked this user. You cannot view their content." -msgstr "" +msgstr "Vous avez bloqué ce compte. Vous ne pouvez pas voir son contenu." #: src/view/com/modals/ModerationDetails.tsx:87 msgid "You have muted this user." -msgstr "" +msgstr "Vous avez masqué ce compte." #: src/view/com/feeds/ProfileFeedgens.tsx:136 msgid "You have no feeds." @@ -3961,7 +3994,7 @@ msgstr "Vous n’avez aucune liste." #: src/view/screens/ModerationBlockedAccounts.tsx:132 msgid "You have not blocked any accounts yet. To block an account, go to their profile and selected \"Block account\" from the menu on their account." -msgstr "Vous n’avez pas encore bloqué de comptes. Pour bloquer un compte, accédez à son profil et sélectionnez « Bloquer le compte » dans le menu de son compte." +msgstr "Vous n’avez pas encore bloqué de comptes. Pour bloquer un compte, accédez à son profil et sélectionnez « Bloquer le compte » dans le menu de son compte." #: src/view/screens/AppPasswords.tsx:87 msgid "You have not created any app passwords yet. You can create one by pressing the button below." @@ -3969,37 +4002,37 @@ msgstr "Vous n’avez encore créé aucun mot de passe pour l’appli. Vous pouv #: src/view/screens/ModerationMutedAccounts.tsx:131 msgid "You have not muted any accounts yet. To mute an account, go to their profile and selected \"Mute account\" from the menu on their account." -msgstr "Vous n’avez encore masqué aucun compte. Pour désactiver un compte, allez sur son profil et sélectionnez « Masquer le compte » dans le menu de son compte." +msgstr "Vous n’avez encore masqué aucun compte. Pour désactiver un compte, allez sur son profil et sélectionnez « Masquer le compte » dans le menu de son compte." #: src/view/com/modals/ContentFilteringSettings.tsx:170 msgid "You must be 18 or older to enable adult content." -msgstr "" +msgstr "Vous devez avoir 18 ans ou plus pour activer le contenu pour adultes." #: src/view/com/util/forms/PostDropdownBtn.tsx:96 msgid "You will no longer receive notifications for this thread" -msgstr "" +msgstr "Vous ne recevrez plus de notifications pour ce fil de discussion" #: src/view/com/util/forms/PostDropdownBtn.tsx:99 msgid "You will now receive notifications for this thread" -msgstr "" +msgstr "Vous recevrez désormais des notifications pour ce fil de discussion" #: src/view/com/auth/login/SetNewPasswordForm.tsx:81 msgid "You will receive an email with a \"reset code.\" Enter that code here, then enter your new password." -msgstr "Vous recevrez un e-mail contenant un « code de réinitialisation » Saisissez ce code ici, puis votre nouveau mot de passe." +msgstr "Vous recevrez un e-mail contenant un « code de réinitialisation » Saisissez ce code ici, puis votre nouveau mot de passe." #: src/view/com/posts/FollowingEndOfFeed.tsx:48 msgid "You've reached the end of your feed! Find some more accounts to follow." -msgstr "" +msgstr "Vous avez atteint la fin de votre fil d’actu ! Trouvez d’autres comptes à suivre." -#: src/view/com/auth/create/Step2.tsx:58 +#: src/view/com/auth/create/Step1.tsx:67 msgid "Your account" msgstr "Votre compte" #: src/view/com/modals/DeleteAccount.tsx:65 msgid "Your account has been deleted" -msgstr "" +msgstr "Votre compte a été supprimé" -#: src/view/com/auth/create/Step2.tsx:146 +#: src/view/com/auth/create/Step1.tsx:182 msgid "Your birth date" msgstr "Votre date de naissance" @@ -4007,14 +4040,14 @@ msgstr "Votre date de naissance" msgid "Your choice will be saved, but can be changed later in settings." msgstr "" -#: src/view/com/auth/create/state.ts:102 +#: src/view/com/auth/create/state.ts:147 #: src/view/com/auth/login/ForgotPasswordForm.tsx:70 msgid "Your email appears to be invalid." msgstr "Votre e-mail semble être invalide." #: src/view/com/modals/Waitlist.tsx:109 msgid "Your email has been saved! We'll be in touch soon." -msgstr "Votre e-mail a été enregistré ! Nous vous contacterons bientôt." +msgstr "Votre e-mail a été enregistré ! Nous vous contacterons bientôt." #: 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." @@ -4026,7 +4059,7 @@ msgstr "Votre e-mail n’a pas encore été vérifié. Il s’agit d’une mesur #: src/view/com/posts/FollowingEmptyState.tsx:47 msgid "Your following feed is empty! Follow more users to see what's happening." -msgstr "" +msgstr "Votre fil d’actu des comptes suivis est vide ! Suivez plus de comptes pour voir ce qui se passe." #: src/view/com/auth/create/Step3.tsx:42 msgid "Your full handle will be" @@ -4034,26 +4067,26 @@ msgstr "Votre nom complet sera" #: src/view/com/modals/ChangeHandle.tsx:270 msgid "Your full handle will be <0>@{0}" -msgstr "" +msgstr "Votre pseudo complet sera <0>@{0}" #: src/view/com/auth/create/Step1.tsx:53 -msgid "Your hosting provider" -msgstr "Votre fournisseur d’hébergement" +#~ msgid "Your hosting provider" +#~ msgstr "Votre hébergeur" #: src/view/screens/Settings.tsx:430 #: src/view/shell/desktop/RightNav.tsx:137 -#: src/view/shell/Drawer.tsx:657 +#: src/view/shell/Drawer.tsx:660 msgid "Your invite codes are hidden when logged in using an App Password" msgstr "Vos codes d’invitation sont cachés lorsque vous êtes connecté à l’aide d’un mot de passe d’application." #: src/view/com/composer/Composer.tsx:267 msgid "Your post has been published" -msgstr "" +msgstr "Votre post a été publié" #: src/view/com/auth/onboarding/WelcomeDesktop.tsx:59 #: src/view/com/auth/onboarding/WelcomeMobile.tsx:59 msgid "Your posts, likes, and blocks are public. Mutes are private." -msgstr "Vos posts, les mentions « J’aime » et les blocages sont publics. Les silences (comptes masqués) sont privés." +msgstr "Vos posts, les likes et les blocages sont publics. Les silences (comptes masqués) sont privés." #: src/view/com/modals/SwitchAccount.tsx:84 #: src/view/screens/Settings.tsx:125 @@ -4062,7 +4095,7 @@ msgstr "Votre profil" #: src/view/com/composer/Composer.tsx:266 msgid "Your reply has been published" -msgstr "" +msgstr "Votre réponse a été publiée" #: src/view/com/auth/create/Step3.tsx:28 msgid "Your user handle" diff --git a/src/locale/locales/hi/messages.po b/src/locale/locales/hi/messages.po index 1240c1cc08..b0f6f984bf 100644 --- a/src/locale/locales/hi/messages.po +++ b/src/locale/locales/hi/messages.po @@ -373,11 +373,6 @@ msgstr "कलात्मक या गैर-कामुक नग्नत #~ msgid "Ask apps to limit the visibility of my account" #~ msgstr "" -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -392,6 +387,11 @@ msgstr "" msgid "Back" msgstr "वापस" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "मूल बातें" @@ -426,6 +426,7 @@ msgstr "खाता ब्लॉक करें?" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -528,15 +529,6 @@ msgstr "कैमरा" 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 "केवल अक्षर, संख्या, रिक्त स्थान, डैश और अंडरस्कोर हो सकते हैं। कम से कम 4 अक्षर लंबा होना चाहिए, लेकिन 32 अक्षरों से अधिक लंबा नहीं होना चाहिए।।" -#: src/view/com/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -555,8 +547,17 @@ msgstr "" msgid "Cancel" msgstr "कैंसिल" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "अकाउंट बंद मत करो" @@ -732,12 +733,6 @@ msgstr "" msgid "Compose reply" msgstr "जवाब लिखो" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -747,6 +742,12 @@ msgstr "" msgid "Confirm" msgstr "हो गया" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -756,7 +757,7 @@ msgstr "बदलाव की पुष्टि करें" msgid "Confirm content language settings" msgstr "सामग्री भाषा सेटिंग्स की पुष्टि करें" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "खाते को हटा दें" @@ -948,7 +949,7 @@ msgstr "अप्प पासवर्ड हटाएं" msgid "Delete List" msgstr "सूची हटाएँ" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "मेरा खाता हटाएं" @@ -1665,7 +1666,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2067,6 +2068,10 @@ msgstr "" msgid "Mute thread" msgstr "थ्रेड म्यूट करें" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "म्यूट किए गए खाते" @@ -2181,11 +2186,6 @@ msgstr "" msgid "Newest replies first" msgstr "" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2196,6 +2196,11 @@ msgstr "" msgid "Next" msgstr "अगला" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "अगली फोटो" @@ -2469,7 +2474,7 @@ msgstr "पृष्ठ नहीं मिला" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "पासवर्ड" @@ -2547,7 +2552,7 @@ msgstr "कृपया इस ऐप पासवर्ड के लिए ए msgid "Please enter your email." msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "कृपया अपना पासवर्ड भी दर्ज करें:" @@ -3414,8 +3419,8 @@ msgid "Subscribe to this list" msgstr "इस सूची को सब्सक्राइब करें" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3549,7 +3554,7 @@ msgstr "" msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -3727,16 +3732,16 @@ msgstr "" msgid "Unable to contact your service. Please check your Internet connection." msgstr "आपकी सेवा से संपर्क करने में असमर्थ। कृपया अपने इंटरनेट कनेक्शन की जांच करें।।" -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" -msgid "Unblock" -msgstr "" - #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "अनब्लॉक" +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" +msgid "Unblock" +msgstr "" + #: src/view/com/profile/ProfileHeader.tsx:308 #: src/view/com/profile/ProfileHeader.tsx:392 msgid "Unblock Account" @@ -3950,6 +3955,10 @@ msgstr "साइट पर जाएं" msgid "Warn" msgstr "" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "" diff --git a/src/locale/locales/id/messages.po b/src/locale/locales/id/messages.po index ba6a4c4be0..f32a00eff1 100644 --- a/src/locale/locales/id/messages.po +++ b/src/locale/locales/id/messages.po @@ -345,11 +345,6 @@ msgstr "" msgid "Artistic or non-erotic nudity." msgstr "Ketelanjangan artistik atau non-erotis." -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -364,6 +359,11 @@ msgstr "" msgid "Back" msgstr "Kembali" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "Dasar" @@ -398,6 +398,7 @@ msgstr "Blokir akun ini?" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -500,15 +501,6 @@ msgstr "Kamera" 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 "Hanya dapat terdiri dari huruf, angka, spasi, tanda hubung dan garis bawah. Minimal 4 karakter, namun tidak boleh lebih dari 32 karakter." -#: src/view/com/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -527,8 +519,17 @@ msgstr "" msgid "Cancel" msgstr "Batal" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "Batal menghapus akun" @@ -704,12 +705,6 @@ msgstr "" msgid "Compose reply" msgstr "Tulis balasan" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -719,6 +714,12 @@ msgstr "" msgid "Confirm" msgstr "Konfirmasi" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -728,7 +729,7 @@ msgstr "Konfirmasi Perubahan" msgid "Confirm content language settings" msgstr "Konfirmasi pengaturan bahasa konten" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "Konfirmasi hapus akun" @@ -920,7 +921,7 @@ msgstr "Hapus kata sandi aplikasi" msgid "Delete List" msgstr "Hapus Daftar " -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "Hapus akun saya" @@ -1624,7 +1625,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2014,6 +2015,10 @@ msgstr "" msgid "Mute thread" msgstr "Bisukan utasan" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "Akun yang dibisukan" @@ -2124,11 +2129,6 @@ msgstr "" msgid "Newest replies first" msgstr "" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2139,6 +2139,11 @@ msgstr "" msgid "Next" msgstr "Berikutnya" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "Gambar berikutnya" @@ -2395,7 +2400,7 @@ msgstr "Halaman tidak ditemukan" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "Kata sandi" @@ -2473,7 +2478,7 @@ msgstr "Mohon masukkan nama unik untuk Kata Sandi Aplikasi ini atau gunakan nama msgid "Please enter your email." msgstr "Masukkan email Anda." -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "Masukkan juga kata sandi Anda:" @@ -3315,8 +3320,8 @@ msgid "Subscribe to this list" msgstr "Langganan ke daftar ini" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3450,7 +3455,7 @@ msgstr "" msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -3628,16 +3633,16 @@ msgstr "Bunyikan daftar" msgid "Unable to contact your service. Please check your Internet connection." msgstr "Tidak dapat terhubung ke layanan. Mohon periksa koneksi internet Anda." -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" -msgid "Unblock" -msgstr "" - #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "Buka blokir" +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" +msgid "Unblock" +msgstr "" + #: src/view/com/profile/ProfileHeader.tsx:308 #: src/view/com/profile/ProfileHeader.tsx:392 msgid "Unblock Account" @@ -3847,6 +3852,10 @@ msgstr "Kunjungi Halaman" msgid "Warn" msgstr "" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "" diff --git a/src/locale/locales/ja/messages.po b/src/locale/locales/ja/messages.po index 7ffca0abcd..1727ded83b 100644 --- a/src/locale/locales/ja/messages.po +++ b/src/locale/locales/ja/messages.po @@ -345,11 +345,6 @@ msgstr "" msgid "Artistic or non-erotic nudity." msgstr "芸術的または性的ではないヌード。" -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -364,6 +359,11 @@ msgstr "" msgid "Back" msgstr "戻る" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "基本" @@ -398,6 +398,7 @@ msgstr "これらのアカウントをブロックしますか?" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -500,15 +501,6 @@ msgstr "カメラ" 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 "文字、数字、スペース、ハイフン、およびアンダースコアのみが使用可能です。長さは4文字以上32文字以下である必要があります。" -#: src/view/com/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -527,8 +519,17 @@ msgstr "" msgid "Cancel" msgstr "キャンセル" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "アカウントの削除をキャンセル" @@ -704,12 +705,6 @@ msgstr "" msgid "Compose reply" msgstr "返信を作成" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -719,6 +714,12 @@ msgstr "" msgid "Confirm" msgstr "確認" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -728,7 +729,7 @@ msgstr "変更を確認" msgid "Confirm content language settings" msgstr "コンテンツの言語設定を確認" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "アカウントの削除を確認" @@ -920,7 +921,7 @@ msgstr "アプリパスワードを削除" msgid "Delete List" msgstr "リストを削除" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "マイアカウントを削除" @@ -1624,7 +1625,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2022,6 +2023,10 @@ msgstr "" msgid "Mute thread" msgstr "スレッドをミュート" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "ミュート中のアカウント" @@ -2132,11 +2137,6 @@ msgstr "" msgid "Newest replies first" msgstr "新しい順に返信を表示" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2147,6 +2147,11 @@ msgstr "" msgid "Next" msgstr "次へ" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "次の画像" @@ -2407,7 +2412,7 @@ msgstr "ページが見つかりません" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "パスワード" @@ -2485,7 +2490,7 @@ msgstr "このアプリパスワードに固有の名前を入力するか、ラ msgid "Please enter your email." msgstr "メールアドレスを入力してください。" -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "パスワードも入力してください:" @@ -3331,8 +3336,8 @@ msgid "Subscribe to this list" msgstr "このリストに登録" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3466,7 +3471,7 @@ msgstr "" msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -3644,16 +3649,16 @@ msgstr "リストでのミュートを解除" msgid "Unable to contact your service. Please check your Internet connection." msgstr "あなたのサービスに接続できません。インターネットの接続を確認してください。" -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" -msgid "Unblock" -msgstr "" - #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "ブロックを解除" +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" +msgid "Unblock" +msgstr "" + #: src/view/com/profile/ProfileHeader.tsx:308 #: src/view/com/profile/ProfileHeader.tsx:392 msgid "Unblock Account" @@ -3863,6 +3868,10 @@ msgstr "サイトへアクセス" msgid "Warn" msgstr "" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "" diff --git a/src/locale/locales/ko/messages.po b/src/locale/locales/ko/messages.po index a0ba65b1ea..277b97e225 100644 --- a/src/locale/locales/ko/messages.po +++ b/src/locale/locales/ko/messages.po @@ -421,11 +421,6 @@ msgstr "선정적이지 않거나 예술적인 노출." #~ msgid "Ask apps to limit the visibility of my account" #~ msgstr "내 계정의 공개 범위를 제한하도록 앱에 요청하기" -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -440,6 +435,11 @@ msgstr "" msgid "Back" msgstr "뒤로" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "기본" @@ -474,6 +474,7 @@ msgstr "이 계정들을 차단하시겠습니까?" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -580,15 +581,6 @@ msgstr "카메라" 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 "글자, 숫자, 공백, 대시, 밑줄만 포함할 수 있습니다. 길이는 4자 이상이어야 하고 32자를 넘지 않아야 합니다." -#: src/view/com/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -607,8 +599,17 @@ msgstr "" msgid "Cancel" msgstr "취소" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "계정 삭제 취소" @@ -788,12 +789,6 @@ msgstr "" msgid "Compose reply" msgstr "답글 작성하기" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -803,6 +798,12 @@ msgstr "" msgid "Confirm" msgstr "확인" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -812,7 +813,7 @@ msgstr "변경 확인" msgid "Confirm content language settings" msgstr "콘텐츠 언어 설정 확인" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "계정 삭제 확인" @@ -1020,7 +1021,7 @@ msgstr "앱 비밀번호 삭제" msgid "Delete List" msgstr "리스트 삭제" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "내 계정 삭제" @@ -1850,7 +1851,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2256,6 +2257,10 @@ msgstr "" msgid "Mute thread" msgstr "스레드 뮤트" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "뮤트한 계정" @@ -2379,11 +2384,6 @@ msgstr "" msgid "Newest replies first" msgstr "새로운 순" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2394,6 +2394,11 @@ msgstr "" msgid "Next" msgstr "다음" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "다음 이미지" @@ -2688,7 +2693,7 @@ msgstr "페이지를 찾을 수 없음" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "비밀번호" @@ -2766,7 +2771,7 @@ msgstr "이 앱 비밀번호에 대해 고유한 이름을 입력하거나 무 msgid "Please enter your email." msgstr "이메일을 입력하세요." -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "비밀번호도 입력해 주세요:" @@ -3683,8 +3688,8 @@ msgid "Subscribe to this list" msgstr "이 리스트로 구독" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3839,7 +3844,7 @@ msgstr "서버에 연결하는 동안 문제가 발생했습니다." msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -4029,16 +4034,16 @@ msgstr "리스트 언뮤트" msgid "Unable to contact your service. Please check your Internet connection." msgstr "서비스에 연결할 수 없습니다. 인터넷 연결을 확인하세요." -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" -msgid "Unblock" -msgstr "" - #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "차단 해제" +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" +msgid "Unblock" +msgstr "" + #: src/view/com/profile/ProfileHeader.tsx:308 #: src/view/com/profile/ProfileHeader.tsx:392 msgid "Unblock Account" @@ -4290,6 +4295,10 @@ msgstr "사이트 방문" msgid "Warn" msgstr "경고" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "이의신청을 즉시 검토하겠습니다." diff --git a/src/locale/locales/pt-BR/messages.po b/src/locale/locales/pt-BR/messages.po index aa2cbee685..d7083ff37b 100644 --- a/src/locale/locales/pt-BR/messages.po +++ b/src/locale/locales/pt-BR/messages.po @@ -373,11 +373,6 @@ msgstr "Nudez artística ou não erótica." #~ msgid "Ask apps to limit the visibility of my account" #~ msgstr "" -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -392,6 +387,11 @@ msgstr "" msgid "Back" msgstr "Voltar" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "Básicos" @@ -426,6 +426,7 @@ msgstr "Bloquear esta conta?" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -528,15 +529,6 @@ msgstr "Câmera" 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 "Só pode conter letras, números, espaços, traços e sublinhados. Deve ter pelo menos 4 caracteres, mas não mais de 32 caracteres." -#: src/view/com/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -555,8 +547,17 @@ msgstr "" msgid "Cancel" msgstr "Cancelar" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "Cancelar exclusão da conta" @@ -736,12 +737,6 @@ msgstr "" msgid "Compose reply" msgstr "Escrever resposta" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -751,6 +746,12 @@ msgstr "" msgid "Confirm" msgstr "Confirme" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -760,7 +761,7 @@ msgstr "Confirmar Alterações" msgid "Confirm content language settings" msgstr "Confirmar configurações de idioma de conteúdo" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "Confirmar a exclusão da conta" @@ -952,7 +953,7 @@ msgstr "Excluir senha do aplicativo" msgid "Delete List" msgstr "Excluir Lista" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "Excluir minha conta" @@ -1673,7 +1674,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2075,6 +2076,10 @@ msgstr "" msgid "Mute thread" msgstr "Silenciar tópico" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "Contas silenciadas" @@ -2189,11 +2194,6 @@ msgstr "" msgid "Newest replies first" msgstr "Respostas mais recentes primeiro" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2204,6 +2204,11 @@ msgstr "" msgid "Next" msgstr "Próximo" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "Próxima imagem" @@ -2477,7 +2482,7 @@ msgstr "Página não encontrada" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "Senha" @@ -2555,7 +2560,7 @@ msgstr "Por favor, insira um nome único para esta Senha do Aplicativo ou use no msgid "Please enter your email." msgstr "Por favor, digite o seu email." -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "Por favor, digite sua senha também:" @@ -3422,8 +3427,8 @@ msgid "Subscribe to this list" msgstr "Assinar esta lista" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3557,7 +3562,7 @@ msgstr "" msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -3735,16 +3740,16 @@ msgstr "Lista de não silenciados" msgid "Unable to contact your service. Please check your Internet connection." msgstr "Não foi possível entrar em contato com seu serviço. Por favor, verifique sua conexão à internet." -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" -msgid "Unblock" -msgstr "" - #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "Desbloquear" +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" +msgid "Unblock" +msgstr "" + #: src/view/com/profile/ProfileHeader.tsx:308 #: src/view/com/profile/ProfileHeader.tsx:392 msgid "Unblock Account" @@ -3958,6 +3963,10 @@ msgstr "Visitar Site" msgid "Warn" msgstr "" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "" diff --git a/src/locale/locales/uk/messages.po b/src/locale/locales/uk/messages.po index ce366a0b38..016e206233 100644 --- a/src/locale/locales/uk/messages.po +++ b/src/locale/locales/uk/messages.po @@ -378,11 +378,6 @@ msgstr "Художня або нееротична оголеність." #~ msgid "Ask apps to limit the visibility of my account" #~ msgstr "" -#: src/view/com/post-thread/PostThread.tsx:400 -msgctxt "action" -msgid "Back" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:141 #: src/view/com/auth/login/ChooseAccountForm.tsx:151 #: src/view/com/auth/login/ForgotPasswordForm.tsx:170 @@ -397,6 +392,11 @@ msgstr "" msgid "Back" msgstr "Назад" +#: src/view/com/post-thread/PostThread.tsx:400 +msgctxt "action" +msgid "Back" +msgstr "" + #: src/view/screens/Settings.tsx:489 msgid "Basics" msgstr "Основні" @@ -431,6 +431,7 @@ msgstr "Заблокувати ці облікові записи?" msgid "Block this List" msgstr "" +#: src/view/com/lists/ListCard.tsx:109 #: src/view/com/util/post-embeds/QuoteEmbed.tsx:57 msgid "Blocked" msgstr "" @@ -533,15 +534,6 @@ msgstr "Камера" 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 "Може містити лише літери, цифри, пробіли, дефіси та знаки підкреслення, і мати довжину від 4 до 32 символів." -#: src/view/com/modals/Confirm.tsx:88 -#: src/view/com/modals/Confirm.tsx:91 -#: src/view/com/modals/CreateOrEditList.tsx:293 -#: src/view/com/modals/DeleteAccount.tsx:152 -#: src/view/com/modals/DeleteAccount.tsx:227 -msgctxt "action" -msgid "Cancel" -msgstr "" - #: src/view/com/composer/Composer.tsx:300 #: src/view/com/composer/Composer.tsx:305 #: src/view/com/modals/ChangeEmail.tsx:218 @@ -560,8 +552,17 @@ msgstr "" msgid "Cancel" msgstr "Скасувати" +#: src/view/com/modals/Confirm.tsx:88 +#: src/view/com/modals/Confirm.tsx:91 +#: src/view/com/modals/CreateOrEditList.tsx:293 +#: src/view/com/modals/DeleteAccount.tsx:152 +#: src/view/com/modals/DeleteAccount.tsx:230 +msgctxt "action" +msgid "Cancel" +msgstr "" + #: src/view/com/modals/DeleteAccount.tsx:148 -#: src/view/com/modals/DeleteAccount.tsx:223 +#: src/view/com/modals/DeleteAccount.tsx:226 msgid "Cancel account deletion" msgstr "Скасувати видалення облікового запису" @@ -741,12 +742,6 @@ msgstr "" msgid "Compose reply" msgstr "Відповісти" -#: src/view/com/modals/Confirm.tsx:75 -#: src/view/com/modals/Confirm.tsx:78 -msgctxt "action" -msgid "Confirm" -msgstr "" - #: src/view/com/modals/AppealLabel.tsx:98 #: src/view/com/modals/SelfLabel.tsx:154 #: src/view/com/modals/VerifyEmail.tsx:231 @@ -756,6 +751,12 @@ msgstr "" msgid "Confirm" msgstr "Підтвердити" +#: src/view/com/modals/Confirm.tsx:75 +#: src/view/com/modals/Confirm.tsx:78 +msgctxt "action" +msgid "Confirm" +msgstr "" + #: src/view/com/modals/ChangeEmail.tsx:193 #: src/view/com/modals/ChangeEmail.tsx:195 msgid "Confirm Change" @@ -765,7 +766,7 @@ msgstr "Підтвердити" msgid "Confirm content language settings" msgstr "Підтвердити перелік мов" -#: src/view/com/modals/DeleteAccount.tsx:213 +#: src/view/com/modals/DeleteAccount.tsx:216 msgid "Confirm delete account" msgstr "Підтвердити видалення облікового запису" @@ -957,7 +958,7 @@ msgstr "Видалити пароль для застосунку" msgid "Delete List" msgstr "Видалити список" -#: src/view/com/modals/DeleteAccount.tsx:216 +#: src/view/com/modals/DeleteAccount.tsx:219 msgid "Delete my account" msgstr "Видалити мій обліковий запис" @@ -1678,7 +1679,7 @@ msgstr "" msgid "Input new password" msgstr "" -#: src/view/com/modals/DeleteAccount.tsx:196 +#: src/view/com/modals/DeleteAccount.tsx:199 msgid "Input password for account deletion" msgstr "" @@ -2080,6 +2081,10 @@ msgstr "" msgid "Mute thread" msgstr "Ігнорувати пост" +#: src/view/com/lists/ListCard.tsx:101 +msgid "Muted" +msgstr "" + #: src/view/screens/Moderation.tsx:109 msgid "Muted accounts" msgstr "Ігноровані облікові записи" @@ -2194,11 +2199,6 @@ msgstr "" msgid "Newest replies first" msgstr "Спочатку найновіші" -#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 -msgctxt "action" -msgid "Next" -msgstr "" - #: src/view/com/auth/create/CreateAccount.tsx:154 #: src/view/com/auth/login/ForgotPasswordForm.tsx:178 #: src/view/com/auth/login/ForgotPasswordForm.tsx:188 @@ -2209,6 +2209,11 @@ msgstr "" msgid "Next" msgstr "Далі" +#: src/view/com/auth/onboarding/WelcomeDesktop.tsx:103 +msgctxt "action" +msgid "Next" +msgstr "" + #: src/view/com/lightbox/Lightbox.web.tsx:142 msgid "Next image" msgstr "Наступне зображення" @@ -2482,7 +2487,7 @@ msgstr "Сторінку не знайдено" #: src/view/com/auth/create/Step2.tsx:132 #: src/view/com/auth/login/LoginForm.tsx:223 #: src/view/com/auth/login/SetNewPasswordForm.tsx:132 -#: src/view/com/modals/DeleteAccount.tsx:195 +#: src/view/com/modals/DeleteAccount.tsx:198 msgid "Password" msgstr "Пароль" @@ -2560,7 +2565,7 @@ msgstr "Будь ласка, введіть унікальну назву для msgid "Please enter your email." msgstr "Будь ласка, введіть адресу ел. пошти." -#: src/view/com/modals/DeleteAccount.tsx:184 +#: src/view/com/modals/DeleteAccount.tsx:187 msgid "Please enter your password as well:" msgstr "Будь ласка, також введіть ваш пароль:" @@ -3427,8 +3432,8 @@ msgid "Subscribe to this list" msgstr "Підписатися на цей список" #: src/view/com/lists/ListCard.tsx:101 -msgid "Subscribed" -msgstr "" +#~ msgid "Subscribed" +#~ msgstr "" #: src/view/screens/Search/Search.tsx:362 msgid "Suggested Follows" @@ -3562,7 +3567,7 @@ msgstr "" msgid "There was an issue fetching notifications. Tap here to try again." msgstr "" -#: src/view/com/posts/Feed.tsx:261 +#: src/view/com/posts/Feed.tsx:263 msgid "There was an issue fetching posts. Tap here to try again." msgstr "" @@ -3740,16 +3745,16 @@ msgstr "Перестати ігнорувати" msgid "Unable to contact your service. Please check your Internet connection." msgstr "Не вдалося зв'язатися з вашим хостинг-провайдером. Перевірте ваше підключення до Інтернету." -#: src/view/com/profile/ProfileHeader.tsx:475 -msgctxt "action" -msgid "Unblock" -msgstr "" - #: src/view/com/profile/ProfileHeader.tsx:472 #: src/view/screens/ProfileList.tsx:568 msgid "Unblock" msgstr "Розблокувати" +#: src/view/com/profile/ProfileHeader.tsx:475 +msgctxt "action" +msgid "Unblock" +msgstr "" + #: src/view/com/profile/ProfileHeader.tsx:308 #: src/view/com/profile/ProfileHeader.tsx:392 msgid "Unblock Account" @@ -3963,6 +3968,10 @@ msgstr "Відвідати сайт" msgid "Warn" msgstr "" +#: src/view/com/posts/DiscoverFallbackHeader.tsx:29 +msgid "We ran out of posts from your follows. Here's the latest from" +msgstr "" + #: src/view/com/modals/AppealLabel.tsx:48 msgid "We'll look into your appeal promptly." msgstr "" diff --git a/src/state/dialogs/index.tsx b/src/state/dialogs/index.tsx new file mode 100644 index 0000000000..4cafaa0861 --- /dev/null +++ b/src/state/dialogs/index.tsx @@ -0,0 +1,44 @@ +import React from 'react' +import {DialogControlProps} from '#/components/Dialog' + +const DialogContext = React.createContext<{ + activeDialogs: React.MutableRefObject< + Map> + > +}>({ + activeDialogs: { + current: new Map(), + }, +}) + +const DialogControlContext = React.createContext<{ + closeAllDialogs(): void +}>({ + closeAllDialogs: () => {}, +}) + +export function useDialogStateContext() { + return React.useContext(DialogContext) +} + +export function useDialogStateControlContext() { + return React.useContext(DialogControlContext) +} + +export function Provider({children}: React.PropsWithChildren<{}>) { + const activeDialogs = React.useRef< + Map> + >(new Map()) + const closeAllDialogs = React.useCallback(() => { + activeDialogs.current.forEach(dialog => dialog.current.close()) + }, []) + const context = React.useMemo(() => ({activeDialogs}), []) + const controls = React.useMemo(() => ({closeAllDialogs}), [closeAllDialogs]) + return ( + + + {children} + + + ) +} diff --git a/src/state/persisted/__tests__/migrate.test.ts b/src/state/persisted/__tests__/migrate.test.ts index d42580efdf..2435ed24fb 100644 --- a/src/state/persisted/__tests__/migrate.test.ts +++ b/src/state/persisted/__tests__/migrate.test.ts @@ -26,7 +26,7 @@ test('migrate: fresh install', async () => { expect(AsyncStorage.getItem).toHaveBeenCalledWith('root') expect(read).toHaveBeenCalledTimes(1) - expect(logger.log).toHaveBeenCalledWith( + expect(logger.info).toHaveBeenCalledWith( 'persisted state: no migration needed', ) }) @@ -38,7 +38,7 @@ test('migrate: fresh install, existing new storage', async () => { expect(AsyncStorage.getItem).toHaveBeenCalledWith('root') expect(read).toHaveBeenCalledTimes(1) - expect(logger.log).toHaveBeenCalledWith( + expect(logger.info).toHaveBeenCalledWith( 'persisted state: no migration needed', ) }) @@ -68,7 +68,7 @@ test('migrate: has legacy data', async () => { await migrate() expect(write).toHaveBeenCalledWith(transform(fixtures.LEGACY_DATA_DUMP)) - expect(logger.log).toHaveBeenCalledWith( + expect(logger.info).toHaveBeenCalledWith( 'persisted state: migrated legacy storage', ) }) diff --git a/src/state/persisted/legacy.ts b/src/state/persisted/legacy.ts index 334ef1d92a..097d6bc5c7 100644 --- a/src/state/persisted/legacy.ts +++ b/src/state/persisted/legacy.ts @@ -164,14 +164,14 @@ export async function migrate() { if (validate.success) { await write(newData) - logger.log('persisted state: migrated legacy storage') + logger.info('persisted state: migrated legacy storage') } else { logger.error('persisted state: legacy data failed validation', { error: validate.error, }) } } else { - logger.log('persisted state: no migration needed') + logger.info('persisted state: no migration needed') } } catch (e: any) { logger.error(e, { diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts index 7a55b4e186..4acc7179a7 100644 --- a/src/state/queries/feed.ts +++ b/src/state/queries/feed.ts @@ -272,7 +272,8 @@ export function usePinnedFeedsInfos(): { }, }) } catch (e) { - logger.warn(`usePinnedFeedsInfos: failed to fetch ${uri}`, { + // expected failure + logger.info(`usePinnedFeedsInfos: failed to fetch ${uri}`, { error: e, }) } diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx index 49bb5a29d3..a96b562258 100644 --- a/src/state/queries/notifications/unread.tsx +++ b/src/state/queries/notifications/unread.tsx @@ -167,7 +167,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { } broadcast.postMessage({event: unreadCountStr}) } catch (e) { - logger.error('Failed to check unread notifications', {error: e}) + logger.warn('Failed to check unread notifications', {error: e}) } }, diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index dbb729133d..82acf39743 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -18,6 +18,7 @@ import {LikesFeedAPI} from 'lib/api/feed/likes' import {CustomFeedAPI} from 'lib/api/feed/custom' import {ListFeedAPI} from 'lib/api/feed/list' import {MergeFeedAPI} from 'lib/api/feed/merge' +import {HomeFeedAPI} from '#/lib/api/feed/home' import {logger} from '#/logger' import {STALE} from '#/state/queries' import {precacheFeedPosts as precacheResolvedUris} from './resolve-uri' @@ -338,7 +339,11 @@ function createApi( feedTuners: FeedTunerFn[], ) { if (feedDesc === 'home') { - return new MergeFeedAPI(params, feedTuners) + if (params.mergeFeedEnabled) { + return new MergeFeedAPI(params, feedTuners) + } else { + return new HomeFeedAPI() + } } else if (feedDesc === 'following') { return new FollowingFeedAPI() } else if (feedDesc.startsWith('author')) { diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index 0a565c9759..e49bc2b390 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -44,6 +44,8 @@ export type ApiContext = { password: string handle: string inviteCode?: string + verificationPhone?: string + verificationCode?: string }) => Promise login: (props: { service: string @@ -203,7 +205,15 @@ export function Provider({children}: React.PropsWithChildren<{}>) { }, [setStateAndPersist, queryClient]) const createAccount = React.useCallback( - async ({service, email, password, handle, inviteCode}: any) => { + async ({ + service, + email, + password, + handle, + inviteCode, + verificationPhone, + verificationCode, + }: any) => { logger.info(`session: creating account`, { service, handle, @@ -217,6 +227,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) { password, email, inviteCode, + verificationPhone, + verificationCode, }) if (!agent.session) { diff --git a/src/view/com/Button.tsx b/src/view/com/Button.tsx deleted file mode 100644 index d1f70d4aeb..0000000000 --- a/src/view/com/Button.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import React from 'react' -import {Pressable, Text, PressableProps, TextProps} from 'react-native' -import * as tokens from '#/alf/tokens' -import {atoms} from '#/alf' - -export type ButtonType = - | 'primary' - | 'secondary' - | 'tertiary' - | 'positive' - | 'negative' -export type ButtonSize = 'small' | 'large' - -export type VariantProps = { - type?: ButtonType - size?: ButtonSize -} -type ButtonState = { - pressed: boolean - hovered: boolean - focused: boolean -} -export type ButtonProps = Omit & - VariantProps & { - children: - | ((props: { - state: ButtonState - type?: ButtonType - size?: ButtonSize - }) => React.ReactNode) - | React.ReactNode - | string - } -export type ButtonTextProps = TextProps & VariantProps - -export function Button({children, style, type, size, ...rest}: ButtonProps) { - const {baseStyles, hoverStyles} = React.useMemo(() => { - const baseStyles = [] - const hoverStyles = [] - - switch (type) { - case 'primary': - baseStyles.push({ - backgroundColor: tokens.color.blue_500, - }) - break - case 'secondary': - baseStyles.push({ - backgroundColor: tokens.color.gray_200, - }) - hoverStyles.push({ - backgroundColor: tokens.color.gray_100, - }) - break - default: - } - - switch (size) { - case 'large': - baseStyles.push( - atoms.py_md, - atoms.px_xl, - atoms.rounded_md, - atoms.gap_sm, - ) - break - case 'small': - baseStyles.push( - atoms.py_sm, - atoms.px_md, - atoms.rounded_sm, - atoms.gap_xs, - ) - break - default: - } - - return { - baseStyles, - hoverStyles, - } - }, [type, size]) - - const [state, setState] = React.useState({ - pressed: false, - hovered: false, - focused: false, - }) - - const onPressIn = React.useCallback(() => { - setState(s => ({ - ...s, - pressed: true, - })) - }, [setState]) - const onPressOut = React.useCallback(() => { - setState(s => ({ - ...s, - pressed: false, - })) - }, [setState]) - const onHoverIn = React.useCallback(() => { - setState(s => ({ - ...s, - hovered: true, - })) - }, [setState]) - const onHoverOut = React.useCallback(() => { - setState(s => ({ - ...s, - hovered: false, - })) - }, [setState]) - const onFocus = React.useCallback(() => { - setState(s => ({ - ...s, - focused: true, - })) - }, [setState]) - const onBlur = React.useCallback(() => { - setState(s => ({ - ...s, - focused: false, - })) - }, [setState]) - - return ( - [ - atoms.flex_row, - atoms.align_center, - ...baseStyles, - ...(state.hovered ? hoverStyles : []), - typeof style === 'function' ? style(state) : style, - ]} - onPressIn={onPressIn} - onPressOut={onPressOut} - onHoverIn={onHoverIn} - onHoverOut={onHoverOut} - onFocus={onFocus} - onBlur={onBlur}> - {typeof children === 'string' ? ( - - {children} - - ) : typeof children === 'function' ? ( - children({state, type, size}) - ) : ( - children - )} - - ) -} - -export function ButtonText({ - children, - style, - type, - size, - ...rest -}: ButtonTextProps) { - const textStyles = React.useMemo(() => { - const base = [] - - switch (type) { - case 'primary': - base.push({color: tokens.color.white}) - break - case 'secondary': - base.push({ - color: tokens.color.gray_700, - }) - break - default: - } - - switch (size) { - case 'small': - base.push(atoms.text_sm, {paddingBottom: 1}) - break - case 'large': - base.push(atoms.text_md, {paddingBottom: 1}) - break - default: - } - - return base - }, [type, size]) - - return ( - - {children} - - ) -} diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx index 74307a631c..449afb0d36 100644 --- a/src/view/com/auth/create/CreateAccount.tsx +++ b/src/view/com/auth/create/CreateAccount.tsx @@ -22,12 +22,13 @@ import { useSetSaveFeedsMutation, DEFAULT_PROD_FEEDS, } from '#/state/queries/preferences' -import {IS_PROD} from '#/lib/constants' +import {FEEDBACK_FORM_URL, IS_PROD} from '#/lib/constants' import {Step1} from './Step1' import {Step2} from './Step2' import {Step3} from './Step3' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' +import {TextLink} from '../../util/Link' export function CreateAccount({onPressBack}: {onPressBack: () => void}) { const {screen} = useAnalytics() @@ -117,7 +118,7 @@ export function CreateAccount({onPressBack}: {onPressBack: () => void}) { return ( @@ -176,6 +177,27 @@ export function CreateAccount({onPressBack}: {onPressBack: () => void}) { ) : undefined} + + + + + Having trouble?{' '} + + + + + diff --git a/src/view/com/auth/create/Step1.tsx b/src/view/com/auth/create/Step1.tsx index 0f8581c0be..2ce77cf530 100644 --- a/src/view/com/auth/create/Step1.tsx +++ b/src/view/com/auth/create/Step1.tsx @@ -1,25 +1,38 @@ import React from 'react' -import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native' +import { + ActivityIndicator, + Keyboard, + StyleSheet, + TouchableWithoutFeedback, + View, +} from 'react-native' +import {CreateAccountState, CreateAccountDispatch, is18} from './state' import {Text} from 'view/com/util/text/Text' +import {DateInput} from 'view/com/util/forms/DateInput' import {StepHeader} from './StepHeader' -import {CreateAccountState, CreateAccountDispatch} from './state' -import {useTheme} from 'lib/ThemeContext' -import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' -import {HelpTip} from '../util/HelpTip' +import {usePalette} from 'lib/hooks/usePalette' import {TextInput} from '../util/TextInput' -import {Button} from 'view/com/util/forms/Button' +import {Button} from '../../util/forms/Button' +import {Policies} from './Policies' import {ErrorMessage} from 'view/com/util/error/ErrorMessage' -import {msg, Trans} from '@lingui/macro' +import {isWeb} from 'platform/detection' +import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useModalControls} from '#/state/modals' +import {logger} from '#/logger' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {LOCAL_DEV_SERVICE, STAGING_SERVICE, PROD_SERVICE} from 'lib/constants' -import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags' +function sanitizeDate(date: Date): Date { + if (!date || date.toString() === 'Invalid Date') { + logger.error(`Create account: handled invalid date for birthDate`, { + hasDate: !!date, + }) + return new Date() + } + return date +} -/** STEP 1: Your hosting provider - * @field Bluesky (default) - * @field Other (staging, local dev, your own PDS, etc.) - */ export function Step1({ uiState, uiDispatch, @@ -28,136 +41,175 @@ export function Step1({ uiDispatch: CreateAccountDispatch }) { const pal = usePalette('default') - const [isDefaultSelected, setIsDefaultSelected] = React.useState(true) const {_} = useLingui() + const {openModal} = useModalControls() - const onPressDefault = React.useCallback(() => { - setIsDefaultSelected(true) - uiDispatch({type: 'set-service-url', value: PROD_SERVICE}) - }, [setIsDefaultSelected, uiDispatch]) + const onPressSelectService = React.useCallback(() => { + openModal({ + name: 'server-input', + initialService: uiState.serviceUrl, + onSelect: (url: string) => + uiDispatch({type: 'set-service-url', value: url}), + }) + Keyboard.dismiss() + }, [uiDispatch, uiState.serviceUrl, openModal]) - const onPressOther = React.useCallback(() => { - setIsDefaultSelected(false) - uiDispatch({type: 'set-service-url', value: 'https://'}) - }, [setIsDefaultSelected, uiDispatch]) + const onPressWaitlist = React.useCallback(() => { + openModal({name: 'waitlist'}) + }, [openModal]) - const onChangeServiceUrl = React.useCallback( - (v: string) => { - uiDispatch({type: 'set-service-url', value: v}) - }, - [uiDispatch], - ) + const birthDate = React.useMemo(() => { + return sanitizeDate(uiState.birthDate) + }, [uiState.birthDate]) return ( - - - This is the service that keeps you online. - - + + {!uiState.serviceDescription ? ( + + ) : ( + <> + {uiState.isInviteCodeRequired && ( + + + Invite code + + uiDispatch({type: 'set-invite-code', value})} + accessibilityLabel={_(msg`Invite code`)} + accessibilityHint={_(msg`Input invite code to proceed`)} + autoCapitalize="none" + autoComplete="off" + autoCorrect={false} + autoFocus={true} /> )} - - + + {!uiState.inviteCode && uiState.isInviteCodeRequired ? ( + + + Don't have an invite code?{' '} + + + + + Join the waitlist. + + + + + ) : ( + <> + + + Email address + + uiDispatch({type: 'set-email', value})} + accessibilityLabel={_(msg`Email`)} + accessibilityHint={_(msg`Input email for Bluesky account`)} + accessibilityLabelledBy="email" + autoCapitalize="none" + autoComplete="off" + autoCorrect={false} + autoFocus={!uiState.isInviteCodeRequired} + /> + + + + + Password + + uiDispatch({type: 'set-password', value})} + accessibilityLabel={_(msg`Password`)} + accessibilityHint={_(msg`Set password`)} + accessibilityLabelledBy="password" + autoCapitalize="none" + autoComplete="off" + autoCorrect={false} + /> + + + + + Your birth date + + + uiDispatch({type: 'set-birth-date', value}) + } + buttonType="default-light" + buttonStyle={[pal.border, styles.dateInputButton]} + buttonLabelType="lg" + accessibilityLabel={_(msg`Birthday`)} + accessibilityHint={_(msg`Enter your birth date`)} + accessibilityLabelledBy="birthDate" + /> + + + {uiState.serviceDescription && ( + + )} + + )} + + )} {uiState.error ? ( - ) : ( - - )} - - ) -} - -function Option({ - children, - isSelected, - label, - help, - onPress, - testID, -}: React.PropsWithChildren<{ - isSelected: boolean - label: string - help?: string - onPress: () => void - testID?: string -}>) { - const theme = useTheme() - const pal = usePalette('default') - const {_} = useLingui() - const circleFillStyle = React.useMemo( - () => ({ - backgroundColor: theme.palette.primary.background, - }), - [theme], - ) - - return ( - - - - - {isSelected ? ( - - ) : undefined} - - - {label} - {help ? ( - - {help} - - ) : undefined} - - - - {isSelected && children} + ) : undefined} ) } @@ -165,34 +217,15 @@ function Option({ const styles = StyleSheet.create({ error: { borderRadius: 6, + marginTop: 10, }, - - option: { + dateInputButton: { borderWidth: 1, borderRadius: 6, - marginBottom: 10, + paddingVertical: 14, }, - optionHeading: { - flexDirection: 'row', - alignItems: 'center', - padding: 10, - }, - circle: { - width: 26, - height: 26, - borderRadius: 15, - padding: 4, - borderWidth: 1, - marginRight: 10, - }, - circleFill: { - width: 16, - height: 16, - borderRadius: 10, - }, - - otherForm: { - paddingBottom: 10, - paddingHorizontal: 12, + // @ts-expect-error: Suppressing error due to incomplete `ViewStyle` type definition in react-native-web, missing `cursor` prop as discussed in https://github.com/necolas/react-native-web/issues/832. + touchable: { + ...(isWeb && {cursor: 'pointer'}), }, }) diff --git a/src/view/com/auth/create/Step2.tsx b/src/view/com/auth/create/Step2.tsx index 53e1e02c9b..f938bb9cea 100644 --- a/src/view/com/auth/create/Step2.tsx +++ b/src/view/com/auth/create/Step2.tsx @@ -1,39 +1,28 @@ import React from 'react' -import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native' -import {CreateAccountState, CreateAccountDispatch, is18} from './state' +import { + ActivityIndicator, + StyleSheet, + TouchableWithoutFeedback, + View, +} from 'react-native' +import { + CreateAccountState, + CreateAccountDispatch, + requestVerificationCode, +} from './state' import {Text} from 'view/com/util/text/Text' -import {DateInput} from 'view/com/util/forms/DateInput' import {StepHeader} from './StepHeader' import {s} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {TextInput} from '../util/TextInput' -import {Policies} from './Policies' +import {Button} from '../../util/forms/Button' 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' -import {logger} from '#/logger' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' +import parsePhoneNumber from 'libphonenumber-js' -function sanitizeDate(date: Date): Date { - if (!date || date.toString() === 'Invalid Date') { - logger.error(`Create account: handled invalid date for birthDate`, { - hasDate: !!date, - }) - return new Date() - } - return date -} - -/** STEP 2: Your account - * @field Invite code or waitlist - * @field Email address - * @field Email address - * @field Email address - * @field Password - * @field Birth date - * @readonly Terms of service & privacy policy - */ export function Step2({ uiState, uiDispatch, @@ -43,130 +32,155 @@ export function Step2({ }) { const pal = usePalette('default') const {_} = useLingui() - const {openModal} = useModalControls() + const {isMobile} = useWebMediaQueries() - const onPressWaitlist = React.useCallback(() => { - openModal({name: 'waitlist'}) - }, [openModal]) + const onPressRequest = React.useCallback(() => { + if ( + uiState.verificationPhone.length >= 9 && + parsePhoneNumber(uiState.verificationPhone, 'US') + ) { + requestVerificationCode({uiState, uiDispatch, _}) + } else { + uiDispatch({ + type: 'set-error', + value: _( + msg`There's something wrong with this number. Please include your country and/or area code!`, + ), + }) + } + }, [uiState, uiDispatch, _]) - const birthDate = React.useMemo(() => { - return sanitizeDate(uiState.birthDate) - }, [uiState.birthDate]) + const onPressRetry = React.useCallback(() => { + uiDispatch({type: 'set-has-requested-verification-code', value: false}) + }, [uiDispatch]) + + const phoneNumberFormatted = React.useMemo( + () => + uiState.hasRequestedVerificationCode + ? parsePhoneNumber( + uiState.verificationPhone, + 'US', + )?.formatInternational() + : '', + [uiState.hasRequestedVerificationCode, uiState.verificationPhone], + ) return ( - + - {uiState.isInviteCodeRequired && ( - - - Invite code - - uiDispatch({type: 'set-invite-code', value})} - accessibilityLabel={_(msg`Invite code`)} - accessibilityHint={_(msg`Input invite code to proceed`)} - autoCapitalize="none" - autoComplete="off" - autoCorrect={false} - /> - - )} + {!uiState.hasRequestedVerificationCode ? ( + <> + + + Phone number + + + uiDispatch({type: 'set-verification-phone', value}) + } + accessibilityLabel={_(msg`Email`)} + accessibilityHint={_( + msg`Input phone number for SMS verification`, + )} + accessibilityLabelledBy="phoneNumber" + keyboardType="phone-pad" + autoCapitalize="none" + autoComplete="tel" + autoCorrect={false} + autoFocus={true} + /> + + + Please enter a phone number that can receive SMS text messages. + + + - {!uiState.inviteCode && uiState.isInviteCodeRequired ? ( - - Don't have an invite code?{' '} - - - - Join the waitlist. - - - - + + {uiState.isProcessing ? ( + + ) : ( + - - - - ) -} - -function BreakpointDebugger() { - const t = useTheme() - const breakpoints = useBreakpoints() - - return ( - -

Breakpoint Debugger

- - Current breakpoint: {!breakpoints.gtMobile && mobile} - {breakpoints.gtMobile && !breakpoints.gtTablet && tablet} - {breakpoints.gtTablet && desktop} - - - {JSON.stringify(breakpoints, null, 2)} - -
- ) -} - -function ThemedSection() { - const t = useTheme() - - return ( - -

theme.atoms.text

- -

- theme.atoms.text_contrast_700 -

- -

- theme.atoms.text_contrast_500 -

- - - - - theme.bg - - - theme.bg_contrast_100 - - - - - theme.bg_contrast_200 - - - theme.bg_contrast_300 - - - - - theme.bg_positive - - - theme.bg_negative - - - - ) -} - -export function DebugScreen() { - const t = useTheme() - - return ( - - - - - - - - - - - - -

Heading 1

-

Heading 2

-

Heading 3

-

Heading 4

-
Heading 5
-
Heading 6
- - atoms.text_xxl - atoms.text_xl - atoms.text_lg - atoms.text_md - atoms.text_sm - atoms.text_xs - atoms.text_xxs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Spacing

- - - - xxs (2px) - - - - - xs (4px) - - - - - sm (8px) - - - - - md (12px) - - - - - lg (18px) - - - - - xl (24px) - - - - - xxl (32px) - - - - - - - -
-
- ) -} diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 0e20a9cf77..7d6a40f022 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -19,7 +19,6 @@ import {useSession} from '#/state/session' import {loadString, saveString} from '#/lib/storage' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {clamp} from '#/lib/numbers' -import {PROD_DEFAULT_FEED} from '#/lib/constants' type Props = NativeStackScreenProps export function HomeScreen(props: Props) { @@ -112,7 +111,7 @@ function HomeScreenReady({ mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled), mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled ? preferences.feeds.saved - : [PROD_DEFAULT_FEED('whats-hot')], + : [], } }, [preferences]) diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx index 8e7e204ad2..94aab2d961 100644 --- a/src/view/screens/Search/Search.tsx +++ b/src/view/screens/Search/Search.tsx @@ -42,9 +42,13 @@ import {useSetDrawerOpen} from '#/state/shell' import {useAnalytics} from '#/lib/analytics/analytics' import {MagnifyingGlassIcon} from '#/lib/icons' import {useModerationOpts} from '#/state/queries/preferences' -import {SearchResultCard} from '#/view/shell/desktop/Search' +import { + MATCH_HANDLE, + SearchLinkCard, + SearchProfileCard, +} from '#/view/shell/desktop/Search' import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell' -import {isWeb} from '#/platform/detection' +import {isNative, isWeb} from '#/platform/detection' import {listenSoftReset} from '#/state/events' import {s} from '#/lib/styles' @@ -83,9 +87,7 @@ function EmptyState({message, error}: {message: string; error?: string}) { }, ]}> - - {message} - + {message} {error && ( <> @@ -511,6 +513,11 @@ export function SearchScreen( onPressCancelSearch() }, [onPressCancelSearch]) + const queryMaybeHandle = React.useMemo(() => { + const match = MATCH_HANDLE.exec(query) + return match && match[1] + }, [query]) + useFocusEffect( React.useCallback(() => { setMinimalShellMode(false) @@ -617,18 +624,31 @@ export function SearchScreen( dataSet={{stableGutters: '1'}} keyboardShouldPersistTaps="handled" keyboardDismissMode="on-drag"> - {searchResults.length ? ( - searchResults.map((item, i) => ( - - )) - ) : ( - - )} + + + {queryMaybeHandle ? ( + + ) : null} + + {searchResults.map(item => ( + + ))} diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index 1f117b45bc..3b50c54493 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -291,7 +291,7 @@ export function SettingsScreen({}: Props) { ]}> - {_(msg`Settings`)} + Settings diff --git a/src/view/screens/Storybook/Breakpoints.tsx b/src/view/screens/Storybook/Breakpoints.tsx new file mode 100644 index 0000000000..1b846d517c --- /dev/null +++ b/src/view/screens/Storybook/Breakpoints.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a, useTheme, useBreakpoints} from '#/alf' +import {Text, H3} from '#/components/Typography' + +export function Breakpoints() { + const t = useTheme() + const breakpoints = useBreakpoints() + + return ( + +

Breakpoint Debugger

+ + Current breakpoint: {!breakpoints.gtMobile && mobile} + {breakpoints.gtMobile && !breakpoints.gtTablet && tablet} + {breakpoints.gtTablet && desktop} + + + {JSON.stringify(breakpoints, null, 2)} + +
+ ) +} diff --git a/src/view/screens/Storybook/Buttons.tsx b/src/view/screens/Storybook/Buttons.tsx new file mode 100644 index 0000000000..fbdc84eb4b --- /dev/null +++ b/src/view/screens/Storybook/Buttons.tsx @@ -0,0 +1,124 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a} from '#/alf' +import { + Button, + ButtonVariant, + ButtonColor, + ButtonIcon, + ButtonText, +} from '#/components/Button' +import {H1} from '#/components/Typography' +import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/ArrowTopRight' +import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' + +export function Buttons() { + return ( + +

Buttons

+ + + {['primary', 'secondary', 'negative'].map(color => ( + + {['solid', 'outline', 'ghost'].map(variant => ( + + + + + ))} + + ))} + + + + {['gradient_sky', 'gradient_midnight', 'gradient_sunrise'].map( + name => ( + + + + + ), + )} + + + {['gradient_sunset', 'gradient_nordic', 'gradient_bonfire'].map( + name => ( + + + + + ), + )} + + + + + + + + + +
+ ) +} diff --git a/src/view/screens/Storybook/Dialogs.tsx b/src/view/screens/Storybook/Dialogs.tsx new file mode 100644 index 0000000000..db568c6bd5 --- /dev/null +++ b/src/view/screens/Storybook/Dialogs.tsx @@ -0,0 +1,90 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a} from '#/alf' +import {Button} from '#/components/Button' +import {H3, P} from '#/components/Typography' +import * as Dialog from '#/components/Dialog' +import * as Prompt from '#/components/Prompt' +import {useDialogStateControlContext} from '#/state/dialogs' + +export function Dialogs() { + const control = Dialog.useDialogControl() + const prompt = Prompt.usePromptControl() + const {closeAllDialogs} = useDialogStateControlContext() + + return ( + + + + + + + This is a prompt + + This is a generic prompt component. It accepts a title and a + description, as well as two actions. + + + Cancel + Confirm + + + + + + + + +

Dialog

+

+ A scrollable dialog with an input within it. +

+ {}} label="Type here" /> + + + + + + + +
+
+
+ ) +} diff --git a/src/view/screens/Storybook/Forms.tsx b/src/view/screens/Storybook/Forms.tsx new file mode 100644 index 0000000000..9396cca67b --- /dev/null +++ b/src/view/screens/Storybook/Forms.tsx @@ -0,0 +1,215 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a} from '#/alf' +import {H1, H3} from '#/components/Typography' +import * as TextField from '#/components/forms/TextField' +import {DateField, Label} from '#/components/forms/DateField' +import * as Toggle from '#/components/forms/Toggle' +import * as ToggleButton from '#/components/forms/ToggleButton' +import {Button} from '#/components/Button' +import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' + +export function Forms() { + const [toggleGroupAValues, setToggleGroupAValues] = React.useState(['a']) + const [toggleGroupBValues, setToggleGroupBValues] = React.useState(['a', 'b']) + const [toggleGroupCValues, setToggleGroupCValues] = React.useState(['a', 'b']) + const [toggleGroupDValues, setToggleGroupDValues] = React.useState(['warn']) + + const [value, setValue] = React.useState('') + const [date, setDate] = React.useState('2001-01-01') + + return ( + +

Forms

+ + +

InputText

+ + + + + + + + + + Text field + + + + @gmail.com + + + + + Textarea + + + +

DateField

+ + + + { + console.log(date) + setDate(date) + }} + label="Input" + /> + +
+ + +

Toggles

+ + + + Uncontrolled toggle + + + + + + + Click me + + + + Click me + + + + Click me + + + + Click me + + + + Click me + + + + + + + + + Click me + + + + Click me + + + + Click me + + + + Click me + + + + Click me + + + + + + + + + Click me + + + + Click me + + + + Click me + + + + Click me + + + + Click me + + + +
+ + + + +

ToggleButton

+ + + + Hide + + + Warn + + + Show + + +
+
+ ) +} diff --git a/src/view/screens/Storybook/Icons.tsx b/src/view/screens/Storybook/Icons.tsx new file mode 100644 index 0000000000..73466e077f --- /dev/null +++ b/src/view/screens/Storybook/Icons.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a, useTheme} from '#/alf' +import {H1} from '#/components/Typography' +import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' +import {ArrowTopRight_Stroke2_Corner0_Rounded as ArrowTopRight} from '#/components/icons/ArrowTopRight' +import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' + +export function Icons() { + const t = useTheme() + return ( + +

Icons

+ + + + + + + + + + + + + + + + + + + + + + + + +
+ ) +} diff --git a/src/view/screens/Storybook/Links.tsx b/src/view/screens/Storybook/Links.tsx new file mode 100644 index 0000000000..c3b1c0e0f3 --- /dev/null +++ b/src/view/screens/Storybook/Links.tsx @@ -0,0 +1,48 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a} from '#/alf' +import {ButtonText} from '#/components/Button' +import {Link} from '#/components/Link' +import {H1, H3} from '#/components/Typography' + +export function Links() { + return ( + +

Links

+ + + + External + + +

External with custom children

+ + + https://blueskyweb.xyz + + + Internal + + + + Link as a button + +
+
+ ) +} diff --git a/src/view/screens/Storybook/Palette.tsx b/src/view/screens/Storybook/Palette.tsx new file mode 100644 index 0000000000..b521fe8607 --- /dev/null +++ b/src/view/screens/Storybook/Palette.tsx @@ -0,0 +1,336 @@ +import React from 'react' +import {View} from 'react-native' + +import * as tokens from '#/alf/tokens' +import {atoms as a} from '#/alf' + +export function Palette() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/view/screens/Storybook/Shadows.tsx b/src/view/screens/Storybook/Shadows.tsx new file mode 100644 index 0000000000..f92112395f --- /dev/null +++ b/src/view/screens/Storybook/Shadows.tsx @@ -0,0 +1,53 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a, useTheme} from '#/alf' +import {H1, Text} from '#/components/Typography' + +export function Shadows() { + const t = useTheme() + + return ( + +

Shadows

+ + + + shadow_sm + + + + shadow_md + + + + shadow_lg + + +
+ ) +} diff --git a/src/view/screens/Storybook/Spacing.tsx b/src/view/screens/Storybook/Spacing.tsx new file mode 100644 index 0000000000..d7faf93a80 --- /dev/null +++ b/src/view/screens/Storybook/Spacing.tsx @@ -0,0 +1,64 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a, useTheme} from '#/alf' +import {Text, H1} from '#/components/Typography' + +export function Spacing() { + const t = useTheme() + return ( + +

Spacing

+ + + 2xs (2px) + + + + + xs (4px) + + + + + sm (8px) + + + + + md (12px) + + + + + lg (16px) + + + + + xl (20px) + + + + + 2xl (24px) + + + + + 3xl (28px) + + + + + 4xl (32px) + + + + + 5xl (40px) + + + + ) +} diff --git a/src/view/screens/Storybook/Theming.tsx b/src/view/screens/Storybook/Theming.tsx new file mode 100644 index 0000000000..a054434734 --- /dev/null +++ b/src/view/screens/Storybook/Theming.tsx @@ -0,0 +1,56 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a, useTheme} from '#/alf' +import {Text} from '#/components/Typography' +import {Palette} from './Palette' + +export function Theming() { + const t = useTheme() + + return ( + + + + theme.atoms.text + + + + theme.atoms.text_contrast_600 + + + + + theme.atoms.text_contrast_500 + + + + + theme.atoms.text_contrast_400 + + + + + + + theme.atoms.bg + + + theme.atoms.bg_contrast_25 + + + theme.atoms.bg_contrast_50 + + + theme.atoms.bg_contrast_100 + + + theme.atoms.bg_contrast_200 + + + theme.atoms.bg_contrast_300 + + + + ) +} diff --git a/src/view/screens/Storybook/Typography.tsx b/src/view/screens/Storybook/Typography.tsx new file mode 100644 index 0000000000..2e1f04a66e --- /dev/null +++ b/src/view/screens/Storybook/Typography.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import {View} from 'react-native' + +import {atoms as a} from '#/alf' +import {Text, H1, H2, H3, H4, H5, H6, P} from '#/components/Typography' + +export function Typography() { + return ( + +

H1 Heading

+

H2 Heading

+

H3 Heading

+

H4 Heading

+
H5 Heading
+
H6 Heading
+

P Paragraph

+ + atoms.text_5xl + atoms.text_4xl + atoms.text_3xl + atoms.text_2xl + atoms.text_xl + atoms.text_lg + atoms.text_md + atoms.text_sm + atoms.text_xs + atoms.text_2xs +
+ ) +} diff --git a/src/view/screens/Storybook/index.tsx b/src/view/screens/Storybook/index.tsx new file mode 100644 index 0000000000..d8898f20e8 --- /dev/null +++ b/src/view/screens/Storybook/index.tsx @@ -0,0 +1,78 @@ +import React from 'react' +import {View} from 'react-native' +import {CenteredView, ScrollView} from '#/view/com/util/Views' + +import {atoms as a, useTheme, ThemeProvider} from '#/alf' +import {useSetColorMode} from '#/state/shell' +import {Button} from '#/components/Button' + +import {Theming} from './Theming' +import {Typography} from './Typography' +import {Spacing} from './Spacing' +import {Buttons} from './Buttons' +import {Links} from './Links' +import {Forms} from './Forms' +import {Dialogs} from './Dialogs' +import {Breakpoints} from './Breakpoints' +import {Shadows} from './Shadows' +import {Icons} from './Icons' + +export function Storybook() { + const t = useTheme() + const setColorMode = useSetColorMode() + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index 6f748755a8..c30874c2f5 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -53,6 +53,8 @@ import {useInviteCodesQuery} from '#/state/queries/invites' import {NavSignupCard} from '#/view/shell/NavSignupCard' import {TextLink} from '../com/util/Link' +import {useTheme as useAlfTheme} from '#/alf' + let DrawerProfileCard = ({ account, onPressProfile, @@ -106,6 +108,7 @@ export {DrawerProfileCard} let DrawerContent = ({}: {}): React.ReactNode => { const theme = useTheme() + const t = useAlfTheme() const pal = usePalette('default') const {_} = useLingui() const setDrawerOpen = useSetDrawerOpen() @@ -208,7 +211,7 @@ let DrawerContent = ({}: {}): React.ReactNode => { testID="drawer" style={[ styles.view, - theme.colorScheme === 'light' ? pal.view : styles.viewDarkMode, + theme.colorScheme === 'light' ? pal.view : t.atoms.bg_contrast_25, ]}> diff --git a/src/view/shell/desktop/Search.tsx b/src/view/shell/desktop/Search.tsx index df49da55b0..4a94837338 100644 --- a/src/view/shell/desktop/Search.tsx +++ b/src/view/shell/desktop/Search.tsx @@ -29,13 +29,63 @@ import {UserAvatar} from '#/view/com/util/UserAvatar' import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete' import {useModerationOpts} from '#/state/queries/preferences' -export function SearchResultCard({ - profile, +export const MATCH_HANDLE = + /@?([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))/ + +export function SearchLinkCard({ + label, + to, + onPress, style, +}: { + label: string + to?: string + onPress?: () => void + style?: ViewStyle +}) { + const pal = usePalette('default') + + const inner = ( + + + {label} + + + ) + + if (onPress) { + return ( + + {inner} + + ) + } + + return ( + + + + {label} + + + + ) +} + +export function SearchProfileCard({ + profile, moderation, }: { profile: AppBskyActorDefs.ProfileViewBasic - style: ViewStyle moderation: ProfileModeration }) { const pal = usePalette('default') @@ -50,9 +100,7 @@ export function SearchResultCard({ { + const match = MATCH_HANDLE.exec(query) + return match && match[1] + }, [query]) + return ( ) : ( <> - {searchResults.length ? ( - searchResults.map((item, i) => ( - - )) - ) : ( - - - No results found for {query} - - - )} + + + {queryMaybeHandle ? ( + + ) : null} + + {searchResults.map(item => ( + + ))} )} diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index 51c03ae3dd..5320aebfc0 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -28,6 +28,7 @@ import {isAndroid} from 'platform/detection' import {useSession} from '#/state/session' import {useCloseAnyActiveElement} from '#/state/util' import * as notifications from 'lib/notifications/notifications' +import {Outlet as PortalOutlet} from '#/components/Portal' function ShellInner() { const isDrawerOpen = useIsDrawerOpen() @@ -94,6 +95,7 @@ function ShellInner() { + ) diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 20bc0dff1f..1ada883c9c 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -15,6 +15,7 @@ import {useAuxClick} from 'lib/hooks/useAuxClick' import {t} from '@lingui/macro' import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell' import {useCloseAllActiveElements} from '#/state/util' +import {Outlet as PortalOutlet} from '#/components/Portal' function ShellInner() { const isDrawerOpen = useIsDrawerOpen() @@ -41,6 +42,7 @@ function ShellInner() { + {!isDesktop && isDrawerOpen && (