fix(auth): require a hashed poll secret for desktop handoff (#2357)

This commit is contained in:
Hampus
2026-09-02 17:25:54 +02:00
committed by GitHub
parent b82681b77a
commit f703969e80
45 changed files with 382 additions and 188 deletions
+35 -1
View File
@@ -9,10 +9,12 @@ import {
AuthTokenWithUserIdResponse,
EmailRevertRequest,
ForgotPasswordRequest,
HandoffCancelRequest,
HandoffCodeParam,
HandoffCompleteRequest,
HandoffInfoResponse,
HandoffInitiateResponse,
HandoffStatusRequest,
HandoffStatusResponse,
IpAuthorizationPollQuery,
IpAuthorizationPollResponse,
@@ -632,10 +634,39 @@ export function AuthController(app: HonoApp) {
return ctx.json(response);
},
);
app.post(
'/auth/handoff/:code/status',
RateLimitMiddleware(RateLimitConfigs.AUTH_HANDOFF_STATUS),
Validator('param', HandoffCodeParam),
Validator('json', HandoffStatusRequest),
OpenAPI({
operationId: 'get_handoff_status_with_secret',
summary: 'Get handoff status with secret',
responseSchema: HandoffStatusResponse,
statusCode: 200,
security: [],
tags: ['Auth'],
description:
'Check the status of a handoff session using the poll secret from initiation. Returns the authentication token once the handoff is complete and the presented secret matches.',
}),
async (ctx) => {
const clientIp = requireClientIp(ctx.req.raw, {
trustClientIpHeader: Config.proxy.trust_client_ip_header,
clientIpHeaderName: Config.proxy.client_ip_header,
});
const response = await ctx.get('authRequestService').getHandoffStatus({
code: ctx.req.valid('param').code,
clientIp,
pollSecret: ctx.req.valid('json').poll_secret,
});
return ctx.json(response);
},
);
app.delete(
'/auth/handoff/:code',
RateLimitMiddleware(RateLimitConfigs.AUTH_HANDOFF_CANCEL),
Validator('param', HandoffCodeParam),
Validator('json', HandoffCancelRequest),
OpenAPI({
operationId: 'cancel_handoff',
summary: 'Cancel handoff',
@@ -646,7 +677,10 @@ export function AuthController(app: HonoApp) {
description: 'Cancel an ongoing handoff session. The handoff code will no longer be valid for authentication.',
}),
async (ctx) => {
await ctx.get('authRequestService').cancelHandoff({code: ctx.req.valid('param').code});
await ctx.get('authRequestService').cancelHandoff({
code: ctx.req.valid('param').code,
pollSecret: ctx.req.valid('json').poll_secret,
});
return ctx.body(null, 204);
},
);
+11 -4
View File
@@ -133,6 +133,12 @@ interface AuthHandoffInfoRequest {
interface AuthHandoffStatusRequest {
code: string;
clientIp: string;
pollSecret?: string;
}
interface AuthHandoffCancelRequest {
code: string;
pollSecret: string;
}
export class AuthRequestService {
@@ -305,6 +311,7 @@ export class AuthRequestService {
return {
code: result.code,
expires_at: result.expiresAt.toISOString(),
poll_secret: result.pollSecret,
};
}
@@ -352,8 +359,8 @@ export class AuthRequestService {
);
}
async getHandoffStatus({code, clientIp}: AuthHandoffStatusRequest): Promise<HandoffStatusResponse> {
const result = await this.desktopHandoffService.getHandoffStatus(code, clientIp);
async getHandoffStatus({code, clientIp, pollSecret}: AuthHandoffStatusRequest): Promise<HandoffStatusResponse> {
const result = await this.desktopHandoffService.getHandoffStatus(code, clientIp, pollSecret);
return {
status: result.status,
token: result.token,
@@ -362,8 +369,8 @@ export class AuthRequestService {
};
}
async cancelHandoff({code}: {code: string}): Promise<void> {
await this.desktopHandoffService.cancelHandoff(code);
async cancelHandoff({code, pollSecret}: AuthHandoffCancelRequest): Promise<void> {
await this.desktopHandoffService.cancelHandoff(code, pollSecret);
}
private async getUserPartial(userId: string): Promise<UserPartialResponse> {
@@ -1,60 +1,84 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {randomBytes} from 'node:crypto';
import {createHash, randomBytes, timingSafeEqual} from 'node:crypto';
import {HandoffCodeExpiredError} from '@fluxer/errors/src/domains/auth/HandoffCodeExpiredError';
import {InvalidHandoffCodeError} from '@fluxer/errors/src/domains/auth/InvalidHandoffCodeError';
import {
DESKTOP_HANDOFF_CODE_ALPHABET,
DESKTOP_HANDOFF_CODE_LENGTH,
formatDesktopHandoffCode,
parseDesktopHandoffCode,
} from '@fluxer/schema/src/domains/auth/DesktopHandoffCode';
import {ms, seconds} from 'itty-time';
import type {ApiContext} from '../../ApiContext';
import type {SessionOrigin} from '../AuthSession';
const HANDOFF_CODE_PREFIX = 'desktop-handoff-v2:';
const HANDOFF_TOKEN_PREFIX = 'desktop-handoff-token:';
const CODE_CHARACTERS = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
const CODE_LENGTH = 12;
const NORMALIZED_CODE_REGEX = /^[ABCDEFGHJKMNPQRSTUVWXYZ23456789]{12}$/;
const HANDOFF_ATTEMPT_PREFIX = 'desktop-handoff-attempts:';
const HANDOFF_APPROVER_PREFIX = 'desktop-handoff-approver:';
const MAX_FAILED_ATTEMPTS = 5;
const ATTEMPT_TTL_SECONDS = 900;
const MAX_INFO_LOOKUPS = 3;
const POLL_SECRET_BYTES = 32;
interface HandoffData {
createdAt: number;
origin: SessionOrigin;
infoLookupCount: number;
pollSecretHash: string;
}
interface HandoffTokenData {
token: string;
userId: string;
pollSecretHash: string;
}
interface HandoffApproverData {
approvedAt: number;
}
function generateHandoffCode(): string {
const maxUnbiased = 256 - (256 % CODE_CHARACTERS.length);
function generateNormalizedHandoffCode(): string {
const maxUnbiased = 256 - (256 % DESKTOP_HANDOFF_CODE_ALPHABET.length);
let code = '';
while (code.length < CODE_LENGTH) {
const bytes = randomBytes(CODE_LENGTH - code.length);
for (let i = 0; i < bytes.length && code.length < CODE_LENGTH; i++) {
while (code.length < DESKTOP_HANDOFF_CODE_LENGTH) {
const bytes = randomBytes(DESKTOP_HANDOFF_CODE_LENGTH - code.length);
for (let i = 0; i < bytes.length && code.length < DESKTOP_HANDOFF_CODE_LENGTH; i++) {
if (bytes[i] < maxUnbiased) {
code += CODE_CHARACTERS[bytes[i] % CODE_CHARACTERS.length];
code += DESKTOP_HANDOFF_CODE_ALPHABET[bytes[i] % DESKTOP_HANDOFF_CODE_ALPHABET.length];
}
}
}
return `${code.slice(0, 6)}-${code.slice(6, 12)}`;
return code;
}
function normalizeHandoffCode(code: string): string {
return code.replace(/[-\s]/g, '').toUpperCase();
}
function assertValidHandoffCode(code: string): void {
if (!NORMALIZED_CODE_REGEX.test(code)) {
function requireNormalizedHandoffCode(code: string): string {
const normalized = parseDesktopHandoffCode(code);
if (normalized == null) {
throw new InvalidHandoffCodeError();
}
return normalized;
}
function generatePollSecret(): string {
return randomBytes(POLL_SECRET_BYTES).toString('base64url');
}
function hashPollSecret(secret: string): string {
return createHash('sha256').update(secret).digest('hex');
}
function pollSecretMatches(presented: string | undefined, storedHash: string | undefined): boolean {
if (!presented || !storedHash) {
return false;
}
const presentedHash = Buffer.from(hashPollSecret(presented), 'hex');
const stored = Buffer.from(storedHash, 'hex');
if (presentedHash.length !== stored.length) {
return false;
}
return timingSafeEqual(presentedHash, stored);
}
export class DesktopHandoffService {
@@ -63,19 +87,21 @@ export class DesktopHandoffService {
async initiateHandoff(args: {origin: SessionOrigin}): Promise<{
code: string;
expiresAt: Date;
pollSecret: string;
}> {
const {cache} = this.apiContext.services;
const code = generateHandoffCode();
const normalizedCode = normalizeHandoffCode(code);
const normalizedCode = generateNormalizedHandoffCode();
const pollSecret = generatePollSecret();
const handoffData: HandoffData = {
createdAt: Date.now(),
origin: args.origin,
infoLookupCount: 0,
pollSecretHash: hashPollSecret(pollSecret),
};
const expirySeconds = seconds('5 minutes');
await cache.set(`${HANDOFF_CODE_PREFIX}${normalizedCode}`, handoffData, expirySeconds);
const expiresAt = new Date(Date.now() + ms('5 minutes'));
return {code, expiresAt};
return {code: formatDesktopHandoffCode(normalizedCode), expiresAt, pollSecret};
}
async completeHandoff(
@@ -84,8 +110,7 @@ export class DesktopHandoffService {
approverIp: string,
): Promise<void> {
const {cache} = this.apiContext.services;
const normalizedCode = normalizeHandoffCode(code);
assertValidHandoffCode(normalizedCode);
const normalizedCode = requireNormalizedHandoffCode(code);
await this.checkAttemptLimit(approverIp);
const storedApprover = await cache.get<HandoffApproverData>(`${HANDOFF_APPROVER_PREFIX}${normalizedCode}`);
if (!storedApprover) {
@@ -108,6 +133,7 @@ export class DesktopHandoffService {
const tokenData: HandoffTokenData = {
token,
userId,
pollSecretHash: handoffData.pollSecretHash,
};
await cache.set(`${HANDOFF_TOKEN_PREFIX}${normalizedCode}`, tokenData, remainingSeconds);
await cache.delete(`${HANDOFF_CODE_PREFIX}${normalizedCode}`);
@@ -122,8 +148,7 @@ export class DesktopHandoffService {
origin?: SessionOrigin;
}> {
const {cache} = this.apiContext.services;
const normalizedCode = normalizeHandoffCode(code);
assertValidHandoffCode(normalizedCode);
const normalizedCode = requireNormalizedHandoffCode(code);
await this.checkAttemptLimit(approverIp);
const codeKey = `${HANDOFF_CODE_PREFIX}${normalizedCode}`;
const handoffData = await cache.get<HandoffData>(codeKey);
@@ -149,18 +174,23 @@ export class DesktopHandoffService {
async getHandoffStatus(
code: string,
_pollerIp: string,
pollerIp: string,
pollSecret: string | undefined,
): Promise<{
status: 'pending' | 'completed' | 'expired';
token?: string;
userId?: string;
}> {
const {cache} = this.apiContext.services;
const normalizedCode = normalizeHandoffCode(code);
assertValidHandoffCode(normalizedCode);
const normalizedCode = requireNormalizedHandoffCode(code);
await this.checkAttemptLimit(pollerIp);
const tokenKey = `${HANDOFF_TOKEN_PREFIX}${normalizedCode}`;
const tokenData = await cache.get<HandoffTokenData>(tokenKey);
if (tokenData) {
if (!pollSecretMatches(pollSecret, tokenData.pollSecretHash)) {
await this.recordFailedAttempt(pollerIp);
return {status: 'pending'};
}
await cache.delete(tokenKey);
return {
status: 'completed',
@@ -175,12 +205,19 @@ export class DesktopHandoffService {
return {status: 'expired'};
}
async cancelHandoff(code: string): Promise<void> {
async cancelHandoff(code: string, pollSecret: string): Promise<void> {
const {cache} = this.apiContext.services;
const normalizedCode = normalizeHandoffCode(code);
assertValidHandoffCode(normalizedCode);
await cache.delete(`${HANDOFF_CODE_PREFIX}${normalizedCode}`);
await cache.delete(`${HANDOFF_TOKEN_PREFIX}${normalizedCode}`);
const normalizedCode = requireNormalizedHandoffCode(code);
const codeKey = `${HANDOFF_CODE_PREFIX}${normalizedCode}`;
const tokenKey = `${HANDOFF_TOKEN_PREFIX}${normalizedCode}`;
const handoffData = await cache.get<HandoffData>(codeKey);
const tokenData = await cache.get<HandoffTokenData>(tokenKey);
const storedHash = handoffData?.pollSecretHash ?? tokenData?.pollSecretHash;
if (!pollSecretMatches(pollSecret, storedHash)) {
throw new InvalidHandoffCodeError();
}
await cache.delete(codeKey);
await cache.delete(tokenKey);
await cache.delete(`${HANDOFF_APPROVER_PREFIX}${normalizedCode}`);
}
@@ -7,6 +7,7 @@ import {createAuthHarness, createTestAccount, loginAccount} from './AuthTestUtil
interface HandoffInitiateResponse {
code: string;
poll_secret: string;
}
interface HandoffInfoResponse {
@@ -59,7 +60,8 @@ describe('Auth desktop handoff code normalization', () => {
.expect(204)
.execute();
const status2 = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.post(`/auth/handoff/${initResp.code}/status`)
.body({poll_secret: initResp.poll_secret})
.execute();
expect(status2.status).toBe('completed');
expect(status2.token).toBeTruthy();
@@ -12,6 +12,7 @@ import {createAuthHarness, createTestAccount, fetchMe, loginAccount} from './Aut
interface HandoffInitiateResponse {
code: string;
expires_at: string;
poll_secret: string;
}
interface HandoffInfoResponse {
@@ -93,7 +94,8 @@ describe('Auth desktop handoff flow', () => {
.expect(204)
.execute();
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.post(`/auth/handoff/${initResp.code}/status`)
.body({poll_secret: initResp.poll_secret})
.execute();
const sessions = await createBuilder<Array<AuthSessionsResponseItem>>(harness, completed.token!)
.get('/auth/sessions')
@@ -116,13 +118,15 @@ describe('Auth desktop handoff flow', () => {
expect(initResp.code).toBeTruthy();
expect(validateHandoffCodeFormat(initResp.code)).toBe(true);
expect(initResp.expires_at).toBeTruthy();
expect(initResp.poll_secret).toBeTruthy();
const info = await createBuilderWithoutAuth<HandoffInfoResponse>(harness)
.get(`/auth/handoff/${initResp.code}/info`)
.execute();
expect(info.status).toBe('pending');
expect(info.client_info).toBeTruthy();
const pending = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.post(`/auth/handoff/${initResp.code}/status`)
.body({poll_secret: initResp.poll_secret})
.execute();
expect(pending.status).toBe('pending');
await createBuilderWithoutAuth(harness)
@@ -135,7 +139,8 @@ describe('Auth desktop handoff flow', () => {
.expect(204)
.execute();
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.post(`/auth/handoff/${initResp.code}/status`)
.body({poll_secret: initResp.poll_secret})
.execute();
expect(completed.status).toBe('completed');
expect(completed.token).toBeTruthy();
@@ -150,7 +155,8 @@ describe('Auth desktop handoff flow', () => {
const handoffUser = handoffSession.json as UserMeResponse;
expect(handoffUser.id).toBe(login.userId);
const retrieved = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.post(`/auth/handoff/${initResp.code}/status`)
.body({poll_secret: initResp.poll_secret})
.execute();
expect(retrieved.status).toBe('expired');
});
@@ -159,7 +165,11 @@ describe('Auth desktop handoff flow', () => {
.post('/auth/handoff/initiate')
.body(null)
.execute();
await createBuilderWithoutAuth(harness).delete(`/auth/handoff/${initResp.code}`).expect(204).execute();
await createBuilderWithoutAuth(harness)
.delete(`/auth/handoff/${initResp.code}`)
.body({poll_secret: initResp.poll_secret})
.expect(204)
.execute();
const cancelled = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.execute();
@@ -184,7 +194,8 @@ describe('Auth desktop handoff flow', () => {
.expect(204)
.execute();
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.post(`/auth/handoff/${initResp.code}/status`)
.body({poll_secret: initResp.poll_secret})
.execute();
expect(completed.status).toBe('completed');
expect(completed.token).toBeTruthy();
@@ -233,8 +244,9 @@ describe('Auth desktop handoff flow', () => {
.expect(204)
.execute();
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.post(`/auth/handoff/${initResp.code}/status`)
.header('x-forwarded-for', pollerIp)
.body({poll_secret: initResp.poll_secret})
.execute();
expect(completed.status).toBe('completed');
expect(completed.user_id).toBe(login.userId);
@@ -267,8 +279,9 @@ describe('Auth desktop handoff flow', () => {
.expect(204)
.execute();
const completed = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.post(`/auth/handoff/${initResp.code}/status`)
.header('x-forwarded-for', pollerIp)
.body({poll_secret: initResp.poll_secret})
.execute();
expect(completed.status).toBe('completed');
expect(completed.user_id).toBe(login.userId);
@@ -72,6 +72,7 @@ describe('Auth desktop handoff negative paths', () => {
it('handles cancel for unknown handoff code gracefully', async () => {
await createBuilderWithoutAuth(harness)
.delete('/auth/handoff/unknown-code')
.body({poll_secret: 'not-the-secret'})
.expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.INVALID_HANDOFF_CODE)
.execute();
});
@@ -7,6 +7,7 @@ import {createAuthHarness, createTestAccount, loginAccount} from './AuthTestUtil
interface HandoffInitiateResponse {
code: string;
poll_secret?: string;
}
interface HandoffStatusResponse {
@@ -56,8 +57,38 @@ describe('Auth desktop handoff complete single use', () => {
const status = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.execute();
expect(status.status).toBe('completed');
expect(status.token).toBeTruthy();
expect(status.token).not.toBe(login.token);
expect(status.status).toBe('pending');
expect(status.token).toBeUndefined();
});
it('releases the token only to a poller presenting the correct secret', async () => {
const account = await createTestAccount(harness);
const login = await loginAccount(harness, account);
const initResp = await createBuilderWithoutAuth<HandoffInitiateResponse>(harness)
.post('/auth/handoff/initiate')
.body(null)
.execute();
expect(initResp.poll_secret).toBeTruthy();
await createBuilderWithoutAuth(harness).get(`/auth/handoff/${initResp.code}/info`).execute();
await createBuilderWithoutAuth(harness)
.post('/auth/handoff/complete')
.header('Authorization', login.token)
.body({
code: initResp.code,
user_id: login.userId,
})
.expect(204)
.execute();
const withoutSecret = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.get(`/auth/handoff/${initResp.code}/status`)
.execute();
expect(withoutSecret.status).toBe('pending');
expect(withoutSecret.token).toBeUndefined();
const withSecret = await createBuilderWithoutAuth<HandoffStatusResponse>(harness)
.post(`/auth/handoff/${initResp.code}/status`)
.body({poll_secret: initResp.poll_secret})
.execute();
expect(withSecret.status).toBe('completed');
expect(withSecret.token).toBeTruthy();
expect(withSecret.token).not.toBe(login.token);
});
});
@@ -113,6 +113,7 @@ export type ResetPasswordResponse = AuthTokenResponse | MfaLoginResponse;
interface DesktopHandoffInitiateResponse {
code: string;
expires_at: string;
poll_secret?: string;
}
interface DesktopHandoffStatusResponse {
@@ -537,9 +538,19 @@ export async function initiateDesktopHandoff(): Promise<DesktopHandoffInitiateRe
return response.body;
}
export async function pollDesktopHandoffStatus(code: string): Promise<DesktopHandoffStatusResponse> {
const response = await http.get<DesktopHandoffStatusResponse>(Endpoints.AUTH_HANDOFF_STATUS(code), {
export async function pollDesktopHandoffStatus(
code: string,
pollSecret?: string | null,
): Promise<DesktopHandoffStatusResponse> {
if (pollSecret == null || pollSecret.length === 0) {
const response = await http.get<DesktopHandoffStatusResponse>(Endpoints.AUTH_HANDOFF_STATUS(code), {
auth: 'none',
});
return response.body;
}
const response = await http.post<DesktopHandoffStatusResponse>(Endpoints.AUTH_HANDOFF_STATUS(code), {
auth: 'none',
body: {poll_secret: pollSecret},
});
return response.body;
}
@@ -38,6 +38,7 @@ const BrowserLoginHandoffModal = observer(({onSuccess, prefillEmail}: BrowserLog
const [isGenerating, setIsGenerating] = useState(false);
const [error, setError] = useState<string | null>(null);
const pollingRef = useRef(false);
const handoffPollSecretRef = useRef<string | null>(null);
const completedRef = useRef(false);
const generateCode = useCallback(async () => {
setIsGenerating(true);
@@ -46,6 +47,7 @@ const BrowserLoginHandoffModal = observer(({onSuccess, prefillEmail}: BrowserLog
setHandoffExpiresAt(null);
try {
const result = await AuthenticationCommands.initiateDesktopHandoff();
handoffPollSecretRef.current = result.poll_secret ?? null;
setHandoffCode(result.code);
setHandoffExpiresAt(result.expires_at);
} catch (e) {
@@ -63,7 +65,7 @@ const BrowserLoginHandoffModal = observer(({onSuccess, prefillEmail}: BrowserLog
const timer = setInterval(async () => {
if (!pollingRef.current) return;
try {
const result = await AuthenticationCommands.pollDesktopHandoffStatus(handoffCode);
const result = await AuthenticationCommands.pollDesktopHandoffStatus(handoffCode, handoffPollSecretRef.current);
if (result.status === 'completed' && result.token && result.user_id) {
pollingRef.current = false;
completedRef.current = true;
@@ -5683,7 +5683,7 @@ msgstr "لا يمكن تحميل الملفات هنا"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "فتح خيارات الشعار"
msgid "Open bookmarks"
msgstr "فتح الإشارات المرجعية"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "فتح المتصفح"
@@ -22680,7 +22680,7 @@ msgstr "فتح رابط الفيديو"
msgid "Open voice"
msgstr "فتح المحادثة الصوتية"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "افتح متصفحك، سجّل الدخول، ثم أدخل الرمز أدناه لربط حسابك."
@@ -36514,7 +36514,7 @@ msgstr "لقد قمنا بتغيير حجم هذه الملصق وضغطه إل
msgid "We sent a code to {email}."
msgstr "أرسلنا رمزًا إلى {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "سنقوم بملء {prefillEmail} مسبقًا بمجرد فتح تسجيل الدخول في المتصفح."
@@ -5683,7 +5683,7 @@ msgstr "Не може да се качват файлове тук"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Отвори опциите за банера"
msgid "Open bookmarks"
msgstr "Отваряне на отметки"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Отваряне на браузъра"
@@ -22680,7 +22680,7 @@ msgstr "Отваряне на видео връзка"
msgid "Open voice"
msgstr "Отваряне на гласов чат"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Отворете браузъра си, влезте в профила си, след което въведете кода по-долу, за да свържете акаунта си."
@@ -36514,7 +36514,7 @@ msgstr "Преоразмерихме и компресирахме този ст
msgid "We sent a code to {email}."
msgstr "Изпратихме код на {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Ще попълним предварително {prefillEmail}, след като се отвори влизането в браузъра."
@@ -5683,7 +5683,7 @@ msgstr "Sem nelze nahrávat soubory"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Otevřít možnosti banneru"
msgid "Open bookmarks"
msgstr "Otevřít záložky"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Otevřít prohlížeč"
@@ -22680,7 +22680,7 @@ msgstr "Otevřít odkaz na video"
msgid "Open voice"
msgstr "Otevřít hlasový chat"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Otevřete prohlížeč, přihlaste se a poté zadejte níže uvedený kód pro propojení účtu."
@@ -36514,7 +36514,7 @@ msgstr "Tento sticker jsme zmenšili a zkomprimovali na 320x320 pixelů, ale st
msgid "We sent a code to {email}."
msgstr "Kód jsme odeslali na {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Předvyplníme {prefillEmail}, jakmile se otevře přihlášení v prohlížeči."
@@ -5683,7 +5683,7 @@ msgstr "Kan ikke uploade filer her"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Åbn bannerindstillinger"
msgid "Open bookmarks"
msgstr "Åbn bogmærker"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Åbn browser"
@@ -22680,7 +22680,7 @@ msgstr "Åbn videolink"
msgid "Open voice"
msgstr "Åbn talechat"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Åbn din browser, log ind, og indtast derefter koden herunder for at forbinde din konto."
@@ -36514,7 +36514,7 @@ msgstr "Vi har ændret størrelsen på og komprimeret dette sticker til 320x320
msgid "We sent a code to {email}."
msgstr "Vi har sendt en kode til {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Vi udfylder automatisk {prefillEmail}, når browser-login åbnes."
@@ -5683,7 +5683,7 @@ msgstr "Dateien können hier nicht hochgeladen werden"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Banner-Optionen öffnen"
msgid "Open bookmarks"
msgstr "Lesezeichen öffnen"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Browser öffnen"
@@ -22680,7 +22680,7 @@ msgstr "Videolink öffnen"
msgid "Open voice"
msgstr "Sprachchat öffnen"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Öffne deinen Browser, melde dich an und gib dann den folgenden Code ein, um deinen Account zu verknüpfen."
@@ -36514,7 +36514,7 @@ msgstr "Wir haben diesen Sticker auf 320x320 Pixel verkleinert und komprimiert,
msgid "We sent a code to {email}."
msgstr "Wir haben einen Code an {email} gesendet."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Wir tragen \"{prefillEmail}\" automatisch ein, sobald sich die Browser-Anmeldung öffnet."
@@ -5683,7 +5683,7 @@ msgstr "Δεν μπορείς να ανεβάσεις αρχεία εδώ"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Άνοιγμα επιλογών banner"
msgid "Open bookmarks"
msgstr "Άνοιγμα σελιδοδεικτών"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Άνοιγμα προγράμματος περιήγησης"
@@ -22680,7 +22680,7 @@ msgstr "Άνοιγμα συνδέσμου βίντεο"
msgid "Open voice"
msgstr "Άνοιγμα φωνητικής συνομιλίας"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Ανοίξτε το πρόγραμμα περιήγησής σας, συνδεθείτε και, στη συνέχεια, εισαγάγετε τον παρακάτω κωδικό για να συνδέσετε τον λογαριασμό σας."
@@ -36514,7 +36514,7 @@ msgstr "Αλλάξαμε το μέγεθος και συμπιέσαμε αυτ
msgid "We sent a code to {email}."
msgstr "Στείλαμε έναν κωδικό στο {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Θα συμπληρώσουμε αυτόματα το {prefillEmail} μόλις ανοίξει η σύνδεση στο πρόγραμμα περιήγησης."
@@ -5683,7 +5683,7 @@ msgstr "Can't upload files here"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Open banner options"
msgid "Open bookmarks"
msgstr "Open bookmarks"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Open browser"
@@ -22680,7 +22680,7 @@ msgstr "Open video link"
msgid "Open voice"
msgstr "Open voice chat"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Open your browser, sign in, then enter the code below to link your account."
@@ -36514,7 +36514,7 @@ msgstr "We resized and compressed this sticker to 320x320 pixels, but it is stil
msgid "We sent a code to {email}."
msgstr "We sent a code to {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "We'll pre-fill {prefillEmail} once browser sign-in opens."
@@ -5684,7 +5684,7 @@ msgstr "Can't upload files here"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22337,7 +22337,7 @@ msgstr "Open banner options"
msgid "Open bookmarks"
msgstr "Open bookmarks"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Open browser"
@@ -22681,7 +22681,7 @@ msgstr "Open video link"
msgid "Open voice"
msgstr "Open voice"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Open your browser, sign in, then enter the code below to link your account."
@@ -36515,7 +36515,7 @@ msgstr "We resized and compressed this sticker to 320x320 pixels, but it is stil
msgid "We sent a code to {email}."
msgstr "We sent a code to {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "We will prefill {prefillEmail} once browser sign-in opens."
@@ -5683,7 +5683,7 @@ msgstr "No se pueden subir archivos aquí"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Abrir opciones del banner"
msgid "Open bookmarks"
msgstr "Abrir marcadores"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Abrir navegador"
@@ -22680,7 +22680,7 @@ msgstr "Abrir enlace de video"
msgid "Open voice"
msgstr "Abrir chat de voz"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Abre tu navegador, inicia sesión y luego ingresa el código a continuación para vincular tu cuenta."
@@ -36514,7 +36514,7 @@ msgstr "Redimensionamos y comprimimos este sticker a 320x320 píxeles, pero sigu
msgid "We sent a code to {email}."
msgstr "Enviamos un código a {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Vamos a rellenar automáticamente {prefillEmail} cuando se abra el inicio de sesión en el navegador."
@@ -5683,7 +5683,7 @@ msgstr "No se pueden subir archivos aquí"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Abrir opciones del banner"
msgid "Open bookmarks"
msgstr "Abrir marcadores"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Abrir navegador"
@@ -22680,7 +22680,7 @@ msgstr "Abrir enlace de vídeo"
msgid "Open voice"
msgstr "Abrir chat de voz"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Abre tu navegador, inicia sesión y, a continuación, introduce el código de abajo para vincular tu cuenta."
@@ -36514,7 +36514,7 @@ msgstr "Hemos redimensionado y comprimido este sticker a 320x320 píxeles, pero
msgid "We sent a code to {email}."
msgstr "Hemos enviado un código a {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Rellenaremos automáticamente {prefillEmail} cuando se abra el inicio de sesión en el navegador."
@@ -5683,7 +5683,7 @@ msgstr "Tiedostojen lähetys ei onnistu tähän"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Avaa banneriasetukset"
msgid "Open bookmarks"
msgstr "Avaa kirjanmerkit"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Avaa selain"
@@ -22680,7 +22680,7 @@ msgstr "Avaa videolinkki"
msgid "Open voice"
msgstr "Avaa puhekanava"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Avaa selaimesi, kirjaudu sisään ja syötä sitten alla oleva koodi tilisi yhdistämiseksi."
@@ -36514,7 +36514,7 @@ msgstr "Pienensimme ja pakkasimme tämän tarran 320x320 pikseliin, mutta sen ko
msgid "We sent a code to {email}."
msgstr "Lähetimme koodin osoitteeseen {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Esitäytämme sähköpostiosoitteen {prefillEmail}, kun kirjautuminen avautuu selaimessa."
@@ -5683,7 +5683,7 @@ msgstr "Impossible de charger des fichiers ici"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Ouvrir les options de la bannière"
msgid "Open bookmarks"
msgstr "Ouvrir les signets"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Ouvrir le navigateur"
@@ -22680,7 +22680,7 @@ msgstr "Ouvrir le lien vidéo"
msgid "Open voice"
msgstr "Ouvrir la discussion vocale"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Ouvrez votre navigateur, connectez-vous, puis saisissez le code ci-dessous pour associer votre compte."
@@ -36514,7 +36514,7 @@ msgstr "Nous avons redimensionné et compressé cet autocollant à 320x320 pixel
msgid "We sent a code to {email}."
msgstr "Nous avons envoyé un code à {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Nous allons préremplir {prefillEmail} une fois que la connexion par navigateur s'ouvrira."
@@ -5683,7 +5683,7 @@ msgstr "לא ניתן להעלות קבצים לכאן"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "אפשרויות באנר"
msgid "Open bookmarks"
msgstr "פתיחת סימניות"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "פתח דפדפן"
@@ -22680,7 +22680,7 @@ msgstr "פתח קישור וידאו"
msgid "Open voice"
msgstr "פתח שיחה קולית"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "פתח את הדפדפן שלך, היכנס, ואז הזן את הקוד למטה כדי לקשר את חשבונך."
@@ -36514,7 +36514,7 @@ msgstr "שינינו את גודל המדבקה ודחסנו אותה ל-320x320
msgid "We sent a code to {email}."
msgstr "שלחנו קוד לכתובת {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "נמלא מראש את {prefillEmail} לאחר שההתחברות בדפדפן תיפתח."
@@ -5683,7 +5683,7 @@ msgstr "यहां फ़ाइलें अपलोड नहीं की
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "बैनर विकल्प खोलें"
msgid "Open bookmarks"
msgstr "बुकमार्क खोलें"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "ब्राउज़र खोलें"
@@ -22680,7 +22680,7 @@ msgstr "वीडियो लिंक खोलें"
msgid "Open voice"
msgstr "वॉइस खोलें"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "अपना ब्राउज़र खोलें, साइन इन करें, फिर अपना अकाउंट लिंक करने के लिए नीचे दिया गया कोड डालें।"
@@ -36514,7 +36514,7 @@ msgstr "हमने इस स्टिकर का आकार बदलक
msgid "We sent a code to {email}."
msgstr "हमने {email} पर एक कोड भेजा है।"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "ब्राउज़र साइन-इन खुलने पर हम {prefillEmail} पहले से भर देंगे।"
@@ -5683,7 +5683,7 @@ msgstr "Ovdje nije moguće učitati datoteke"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Otvori opcije natpisa"
msgid "Open bookmarks"
msgstr "Otvori oznake"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Otvori preglednik"
@@ -22680,7 +22680,7 @@ msgstr "Otvori videopoveznicu"
msgid "Open voice"
msgstr "Otvori glasovni chat"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Otvorite preglednik, prijavite se, a zatim unesite kôd u nastavku za povezivanje računa."
@@ -36514,7 +36514,7 @@ msgstr "Promijenili smo veličinu i komprimirali ovu naljepnicu na 320x320 pikse
msgid "We sent a code to {email}."
msgstr "Poslali smo kôd na adresu {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Automatski ćemo popuniti {prefillEmail} kada se otvori prijava u pregledniku."
@@ -5683,7 +5683,7 @@ msgstr "Ide nem tölthetsz fel fájlokat"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Bannerbeállítások megnyitása"
msgid "Open bookmarks"
msgstr "Könyvjelzők megnyitása"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Böngésző megnyitása"
@@ -22680,7 +22680,7 @@ msgstr "Videólink megnyitása"
msgid "Open voice"
msgstr "Hangüzenet megnyitása"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Nyissa meg a böngészőjét, jelentkezzen be, majd adja meg az alábbi kódot a fiókja összekapcsolásához."
@@ -36514,7 +36514,7 @@ msgstr "Átméreteztük és tömörítettük ezt a matricát 320x320 képpontra,
msgid "We sent a code to {email}."
msgstr "Kódot küldtünk a következő címre: {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Előre kitöltjük a(z) {prefillEmail} címet, amint megnyílik a böngészős bejelentkezés."
@@ -5683,7 +5683,7 @@ msgstr "Tidak bisa mengunggah file di sini"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Buka opsi banner"
msgid "Open bookmarks"
msgstr "Buka markah"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Buka browser"
@@ -22680,7 +22680,7 @@ msgstr "Buka tautan video"
msgid "Open voice"
msgstr "Buka obrolan suara"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Buka browser Anda, masuk, lalu masukkan kode di bawah untuk menautkan akun Anda."
@@ -36514,7 +36514,7 @@ msgstr "Kami mengubah ukuran dan mengompres stiker ini menjadi 320x320 piksel, t
msgid "We sent a code to {email}."
msgstr "Kami telah mengirim kode ke {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Kami akan mengisi otomatis {prefillEmail} setelah login browser terbuka."
@@ -5683,7 +5683,7 @@ msgstr "Impossibile caricare file qui"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Apri opzioni banner"
msgid "Open bookmarks"
msgstr "Apri i preferiti"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Apri il browser"
@@ -22680,7 +22680,7 @@ msgstr "Apri link video"
msgid "Open voice"
msgstr "Apri la chat vocale"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Apri il browser, accedi, quindi inserisci il codice qui sotto per collegare il tuo account."
@@ -36514,7 +36514,7 @@ msgstr "Abbiamo ridimensionato e compresso questo sticker a 320x320 pixel, ma è
msgid "We sent a code to {email}."
msgstr "Abbiamo inviato un codice a {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Precompileremo {prefillEmail} quando si aprirà l'accesso tramite browser."
@@ -5683,7 +5683,7 @@ msgstr "ここにファイルをアップロードできません"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "バナーオプションを開く"
msgid "Open bookmarks"
msgstr "ブックマークを開く"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "ブラウザを開く"
@@ -22680,7 +22680,7 @@ msgstr "動画リンクを開く"
msgid "Open voice"
msgstr "音声チャットを開始"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "ブラウザを開いてサインインし、以下のコードを入力してアカウントをリンクしてください。"
@@ -36514,7 +36514,7 @@ msgstr "このスタンプは320x320ピクセルにリサイズおよび圧縮
msgid "We sent a code to {email}."
msgstr "{email} にコードを送信しました。"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "ブラウザでサインイン画面が開いたら、{prefillEmail}を自動入力します。"
@@ -5683,7 +5683,7 @@ msgstr "여기에 파일을 올릴 수 없어요"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "배너 옵션 열기"
msgid "Open bookmarks"
msgstr "북마크 열기"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "브라우저 열기"
@@ -22680,7 +22680,7 @@ msgstr "동영상 링크 열기"
msgid "Open voice"
msgstr "음성 채팅 시작"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "브라우저를 열고 로그인한 다음 아래 코드를 입력하여 계정을 연결하세요."
@@ -36514,7 +36514,7 @@ msgstr "스티커 크기를 320x320픽셀로 조정하고 압축했지만, 여
msgid "We sent a code to {email}."
msgstr "{email}으로 코드를 전송했습니다."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "브라우저 로그인 창이 열리면 {prefillEmail}이(가) 자동으로 입력됩니다."
@@ -5683,7 +5683,7 @@ msgstr "Čia negalima įkelti failų"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Atidaryti reklamjuostės parinktis"
msgid "Open bookmarks"
msgstr "Atidaryti žymes"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Atidaryti naršyklę"
@@ -22680,7 +22680,7 @@ msgstr "Atidaryti vaizdo įrašo nuorodą"
msgid "Open voice"
msgstr "Atidaryti balso pokalbį"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Atidarykite naršyklę, prisijunkite, tada įveskite toliau pateiktą kodą, kad susietumėte paskyrą."
@@ -36514,7 +36514,7 @@ msgstr "Šį lipduką pakeitėme ir suglaudinome iki 320x320 pikselių, bet jo d
msgid "We sent a code to {email}."
msgstr "Išsiuntėme kodą į {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Atidarius prisijungimo naršyklėje langą, iš anksto užpildysime {prefillEmail}."
@@ -5683,7 +5683,7 @@ msgstr "Kan hier geen bestanden uploaden"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Banneropties openen"
msgid "Open bookmarks"
msgstr "Bladwijzers openen"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Browser openen"
@@ -22680,7 +22680,7 @@ msgstr "Videolink openen"
msgid "Open voice"
msgstr "Spraak openen"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Open je browser, meld je aan en voer de onderstaande code in om je account te koppelen."
@@ -36514,7 +36514,7 @@ msgstr "We hebben deze sticker verkleind en gecomprimeerd naar 320x320 pixels, m
msgid "We sent a code to {email}."
msgstr "We hebben een code gestuurd naar {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "We vullen {prefillEmail} alvast in zodra het inlogscherm in je browser opent."
@@ -5683,7 +5683,7 @@ msgstr "Kan ikke laste opp filer her"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Åpne bannerinnstillinger"
msgid "Open bookmarks"
msgstr "Åpne bokmerker"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Åpne nettleser"
@@ -22680,7 +22680,7 @@ msgstr "Åpne videolenke"
msgid "Open voice"
msgstr "Åpne talechat"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Åpne nettleseren din, logg på, og skriv inn koden nedenfor for å koble til kontoen din."
@@ -36514,7 +36514,7 @@ msgstr "Vi endret størrelsen og komprimerte dette klistremerket til 320x320 pik
msgid "We sent a code to {email}."
msgstr "Vi sendte en kode til {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Vi fyller ut {prefillEmail} automatisk når nettleserpåloggingen åpnes."
@@ -5683,7 +5683,7 @@ msgstr "Nie można tu przesyłać plików"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Otwórz opcje banera"
msgid "Open bookmarks"
msgstr "Otwórz zakładki"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Otwórz przeglądarkę"
@@ -22680,7 +22680,7 @@ msgstr "Otwórz link do wideo"
msgid "Open voice"
msgstr "Otwórz czat głosowy"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Otwórz przeglądarkę, zaloguj się, a następnie wprowadź poniższy kod, aby połączyć swoje konto."
@@ -36514,7 +36514,7 @@ msgstr "Zmieniliśmy rozmiar i skompresowaliśmy tę naklejkę do 320x320 piksel
msgid "We sent a code to {email}."
msgstr "Wysłaliśmy kod na adres {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Wypełnimy wstępnie pole adresem {prefillEmail}, gdy otworzy się logowanie w przeglądarce."
@@ -5683,7 +5683,7 @@ msgstr "Não é possível enviar arquivos aqui"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Abrir opções do banner"
msgid "Open bookmarks"
msgstr "Abrir favoritos"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Abrir navegador"
@@ -22680,7 +22680,7 @@ msgstr "Abrir link do vídeo"
msgid "Open voice"
msgstr "Abrir áudio"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Abra seu navegador, faça login e insira o código abaixo para vincular sua conta."
@@ -36514,7 +36514,7 @@ msgstr "Redimensionamos e compactamos este sticker para 320x320 pixels, mas ele
msgid "We sent a code to {email}."
msgstr "Enviamos um código para {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Vamos preencher automaticamente {prefillEmail} assim que o login no navegador abrir."
@@ -5683,7 +5683,7 @@ msgstr "Nu se pot încărca fișiere aici"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Deschide opțiunile bannerului"
msgid "Open bookmarks"
msgstr "Deschide marcajele"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Deschide browserul"
@@ -22680,7 +22680,7 @@ msgstr "Deschide linkul video"
msgid "Open voice"
msgstr "Deschide mesajul vocal"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Deschide browserul, conectează-te, apoi introdu codul de mai jos pentru a-ți asocia contul."
@@ -36514,7 +36514,7 @@ msgstr "Am redimensionat și am comprimat acest sticker la 320x320 pixeli, dar a
msgid "We sent a code to {email}."
msgstr "Am trimis un cod la {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Vom precompleta {prefillEmail} odată ce se deschide autentificarea în browser."
@@ -5683,7 +5683,7 @@ msgstr "Не удается загрузить файлы сюда"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Открыть настройки баннера"
msgid "Open bookmarks"
msgstr "Открыть закладки"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Открыть браузер"
@@ -22680,7 +22680,7 @@ msgstr "Открыть ссылку на видео"
msgid "Open voice"
msgstr "Открыть голосовой чат"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Откройте браузер, войдите в аккаунт, затем введите код ниже, чтобы привязать свою учётную запись."
@@ -36514,7 +36514,7 @@ msgstr "Мы изменили размер и сжали этот стикер
msgid "We sent a code to {email}."
msgstr "Мы отправили код на {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Мы автоматически заполним поле с адресом {prefillEmail}, как только откроется окно входа в браузере."
@@ -5683,7 +5683,7 @@ msgstr "Kan inte ladda upp filer här"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Öppna banneralternativ"
msgid "Open bookmarks"
msgstr "Öppna bokmärken"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Öppna webbläsaren"
@@ -22680,7 +22680,7 @@ msgstr "Öppna videolänk"
msgid "Open voice"
msgstr "Öppna röstchatt"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Öppna din webbläsare, logga in och ange sedan koden nedan för att länka ditt konto."
@@ -36514,7 +36514,7 @@ msgstr "Vi ändrade storlek och komprimerade den här stickern till 320x320 pixl
msgid "We sent a code to {email}."
msgstr "Vi skickade en kod till {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Vi fyller i {prefillEmail} automatiskt när inloggningen öppnas i webbläsaren."
@@ -5683,7 +5683,7 @@ msgstr "อัปโหลดไฟล์ที่นี่ไม่ได้"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "เปิดตัวเลือกแบนเนอร์"
msgid "Open bookmarks"
msgstr "เปิดบุ๊กมาร์ก"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "เปิดเบราว์เซอร์"
@@ -22680,7 +22680,7 @@ msgstr "เปิดลิงก์วิดีโอ"
msgid "Open voice"
msgstr "เปิดเสียง"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "เปิดเบราว์เซอร์ของคุณ ลงชื่อเข้าใช้ แล้วป้อนรหัสข้างล่างเพื่อเชื่อมโยงบัญชีของคุณ"
@@ -36514,7 +36514,7 @@ msgstr "เราได้ปรับขนาดและบีบอัดส
msgid "We sent a code to {email}."
msgstr "เราได้ส่งรหัสไปยัง {email} แล้ว"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "เราจะกรอก {prefillEmail} ให้โดยอัตโนมัติเมื่อเปิดหน้าลงชื่อเข้าใช้ในเบราว์เซอร์"
@@ -5683,7 +5683,7 @@ msgstr "Buraya dosya yüklenemiyor"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Banner seçeneklerini aç"
msgid "Open bookmarks"
msgstr "Yer işaretlerini aç"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Tarayıcıyı aç"
@@ -22680,7 +22680,7 @@ msgstr "Video bağlantısını aç"
msgid "Open voice"
msgstr "Sesi aç"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Tarayıcınızı açın, oturum açın ve hesabınızı bağlamak için aşağıdaki kodu girin."
@@ -36514,7 +36514,7 @@ msgstr "Bu çıkartmayı 320x320 piksele yeniden boyutlandırıp sıkıştırdı
msgid "We sent a code to {email}."
msgstr "{email} adresine bir kod gönderdik."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Tarayıcıda oturum açma ekranı açıldığında {prefillEmail} adresini önceden dolduracağız."
@@ -5683,7 +5683,7 @@ msgstr "Не вдається завантажити файли сюди"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Відкрити параметри банера"
msgid "Open bookmarks"
msgstr "Відкрити закладки"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Відкрити браузер"
@@ -22680,7 +22680,7 @@ msgstr "Відкрити посилання на відео"
msgid "Open voice"
msgstr "Відкрити голосовий чат"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Відкрийте браузер, увійдіть, а потім введіть код нижче, щоб зв'язати свій обліковий запис."
@@ -36514,7 +36514,7 @@ msgstr "Ми змінили розмір і стиснули цей стікер
msgid "We sent a code to {email}."
msgstr "Ми надіслали код на {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Ми попередньо заповнимо {prefillEmail}, щойно відкриється вхід у браузері."
@@ -5683,7 +5683,7 @@ msgstr "Không thể tải tệp lên đây"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "Mở tùy chọn biểu ngữ"
msgid "Open bookmarks"
msgstr "Mở dấu trang"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "Mở trình duyệt"
@@ -22680,7 +22680,7 @@ msgstr "Mở liên kết video"
msgid "Open voice"
msgstr "Mở trò chuyện thoại"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "Mở trình duyệt, đăng nhập, sau đó nhập mã bên dưới để liên kết tài khoản của bạn."
@@ -36514,7 +36514,7 @@ msgstr "Chúng tôi đã đổi kích thước và nén nhãn dán này thành 3
msgid "We sent a code to {email}."
msgstr "Chúng tôi đã gửi mã đến {email}."
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "Chúng tôi sẽ điền trước {prefillEmail} sau khi trình duyệt đăng nhập mở."
@@ -5683,7 +5683,7 @@ msgstr "无法在此上传文件"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "打开横幅选项"
msgid "Open bookmarks"
msgstr "打开书签"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "打开浏览器"
@@ -22680,7 +22680,7 @@ msgstr "打开视频链接"
msgid "Open voice"
msgstr "开启语音"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "打开浏览器,登录后输入以下代码以关联你的帐户。"
@@ -36514,7 +36514,7 @@ msgstr "我们已将此贴纸调整并压缩到 320x320 像素,但它仍然是
msgid "We sent a code to {email}."
msgstr "验证码已发送至 {email}。"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "浏览器登录页面打开后,我们将预填 {prefillEmail}。"
@@ -5683,7 +5683,7 @@ msgstr "無法在這裡上傳檔案"
#: src/features/auth/components/modals/PasskeyNameModal.tsx:80
#: src/features/auth/components/modals/PasskeyPinModal.tsx:104
#: src/features/auth/components/modals/SudoVerificationModal.tsx:263
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:138
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:140
#: src/features/channel/components/bottomsheets/CreateDMBottomSheet.tsx:137
#: src/features/channel/components/MentionEveryonePopout.tsx:173
#: src/features/channel/components/modals/ChannelDeleteModal.tsx:53
@@ -22336,7 +22336,7 @@ msgstr "開啟橫幅選項"
msgid "Open bookmarks"
msgstr "開啟書籤"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:151
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:153
msgid "Open browser"
msgstr "開啟瀏覽器"
@@ -22680,7 +22680,7 @@ msgstr "開啟影片連結"
msgid "Open voice"
msgstr "開啟語音"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:111
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:113
msgid "Open your browser, sign in, then enter the code below to link your account."
msgstr "開啟您的瀏覽器,登入後輸入以下代碼,即可連結您的帳號。"
@@ -36514,7 +36514,7 @@ msgstr "我們已將此貼圖調整大小並壓縮至 320x320 像素,但它仍
msgid "We sent a code to {email}."
msgstr "我們已將驗證碼傳送至 {email}。"
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:126
#: src/features/auth/flow/BrowserLoginHandoffModal.tsx:128
msgid "We will prefill {prefillEmail} once browser sign-in opens."
msgstr "瀏覽器登入開啟後,我們會預填 {prefillEmail}。"
@@ -227,6 +227,7 @@ export type UsernameSuggestionsResponse = z.infer<typeof UsernameSuggestionsResp
export const HandoffInitiateResponse = z.object({
code: z.string().describe('Handoff code to share with the receiving device'),
expires_at: z.iso.datetime().describe('ISO 8601 timestamp when the handoff code expires'),
poll_secret: z.string().optional().describe('Secret the initiating device must present to retrieve the token'),
});
export type HandoffInitiateResponse = z.infer<typeof HandoffInitiateResponse>;
@@ -331,6 +332,18 @@ export const HandoffCodeParam = z.object({
export type HandoffCodeParam = z.infer<typeof HandoffCodeParam>;
export const HandoffStatusRequest = z.object({
poll_secret: createStringType().describe('The poll secret issued when the handoff was initiated'),
});
export type HandoffStatusRequest = z.infer<typeof HandoffStatusRequest>;
export const HandoffCancelRequest = z.object({
poll_secret: createStringType().describe('The poll secret issued when the handoff was initiated'),
});
export type HandoffCancelRequest = z.infer<typeof HandoffCancelRequest>;
export const EnableMfaTotpRequest = z
.object({
secret: createStringType(1, 256).describe('The TOTP secret key'),
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
export const DESKTOP_HANDOFF_CODE_ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
export const DESKTOP_HANDOFF_CODE_LENGTH = 12;
const DESKTOP_HANDOFF_CODE_GROUP_LENGTH = 6;
const DESKTOP_HANDOFF_CODE_SEPARATOR = '-';
const DESKTOP_HANDOFF_CODE_SEPARATOR_PATTERN = /[^A-Za-z0-9]/gu;
function normalizeDesktopHandoffCode(value: string): string {
return value.replace(DESKTOP_HANDOFF_CODE_SEPARATOR_PATTERN, '').toUpperCase();
}
export function isDesktopHandoffCode(value: string): boolean {
if (value.length !== DESKTOP_HANDOFF_CODE_LENGTH) {
return false;
}
return Array.from(value).every((character) => DESKTOP_HANDOFF_CODE_ALPHABET.includes(character));
}
export function parseDesktopHandoffCodeInput(value: string): string {
return normalizeDesktopHandoffCode(value).slice(0, DESKTOP_HANDOFF_CODE_LENGTH);
}
export function parseDesktopHandoffCode(value: string | null | undefined): string | null {
if (value == null) {
return null;
}
const normalized = normalizeDesktopHandoffCode(value);
if (!isDesktopHandoffCode(normalized)) {
return null;
}
return normalized;
}
export function formatDesktopHandoffCode(value: string): string {
const normalized = parseDesktopHandoffCodeInput(value);
const groups: Array<string> = [];
for (let index = 0; index < normalized.length; index += DESKTOP_HANDOFF_CODE_GROUP_LENGTH) {
groups.push(normalized.slice(index, index + DESKTOP_HANDOFF_CODE_GROUP_LENGTH));
}
return groups.join(DESKTOP_HANDOFF_CODE_SEPARATOR);
}