diff --git a/.changeset/brave-countries-return.md b/.changeset/brave-countries-return.md new file mode 100644 index 000000000..4ce768d17 --- /dev/null +++ b/.changeset/brave-countries-return.md @@ -0,0 +1,5 @@ +--- +"@atproto/oauth-provider": patch +--- + +Add support for password reset diff --git a/.changeset/eleven-ducks-boil.md b/.changeset/eleven-ducks-boil.md new file mode 100644 index 000000000..cdd84f0ba --- /dev/null +++ b/.changeset/eleven-ducks-boil.md @@ -0,0 +1,5 @@ +--- +"@atproto-labs/fetch": patch +--- + +Improved error response parsing diff --git a/.changeset/hip-feet-play.md b/.changeset/hip-feet-play.md new file mode 100644 index 000000000..a0ba6e08f --- /dev/null +++ b/.changeset/hip-feet-play.md @@ -0,0 +1,5 @@ +--- +"@atproto/oauth-provider": minor +--- + +Add support for account sign-up diff --git a/.changeset/hungry-buttons-clean.md b/.changeset/hungry-buttons-clean.md new file mode 100644 index 000000000..21869c2c6 --- /dev/null +++ b/.changeset/hungry-buttons-clean.md @@ -0,0 +1,5 @@ +--- +"@atproto/oauth-client-browser-example": patch +--- + +Update react to version 19 diff --git a/.changeset/long-bats-guess.md b/.changeset/long-bats-guess.md new file mode 100644 index 000000000..80a0de911 --- /dev/null +++ b/.changeset/long-bats-guess.md @@ -0,0 +1,7 @@ +--- +"@atproto/oauth-provider": patch +"@atproto/oauth-types": patch +"@atproto/jwk": patch +--- + +Properly support locales with 3 chars (Asturian) diff --git a/.changeset/rotten-hornets-develop.md b/.changeset/rotten-hornets-develop.md new file mode 100644 index 000000000..54d87b47b --- /dev/null +++ b/.changeset/rotten-hornets-develop.md @@ -0,0 +1,5 @@ +--- +"@atproto/oauth-provider": patch +--- + +Add support for multiple locales diff --git a/.changeset/short-masks-punch.md b/.changeset/short-masks-punch.md new file mode 100644 index 000000000..950ecb518 --- /dev/null +++ b/.changeset/short-masks-punch.md @@ -0,0 +1,5 @@ +--- +"@atproto/syntax": patch +--- + +Deprecate unused classes diff --git a/.changeset/sour-guests-work.md b/.changeset/sour-guests-work.md new file mode 100644 index 000000000..553a69de0 --- /dev/null +++ b/.changeset/sour-guests-work.md @@ -0,0 +1,5 @@ +--- +"@atproto-labs/rollup-plugin-bundle-manifest": patch +--- + +Improve typing of plugin diff --git a/.changeset/tall-rules-hammer.md b/.changeset/tall-rules-hammer.md new file mode 100644 index 000000000..9f70720c7 --- /dev/null +++ b/.changeset/tall-rules-hammer.md @@ -0,0 +1,5 @@ +--- +"@atproto/oauth-client": patch +--- + +Minor code optimizations diff --git a/.changeset/tiny-goats-sing.md b/.changeset/tiny-goats-sing.md new file mode 100644 index 000000000..3fab228e9 --- /dev/null +++ b/.changeset/tiny-goats-sing.md @@ -0,0 +1,5 @@ +--- +"@atproto-labs/fetch": patch +--- + +Remove explicit dependency on "zod". Improved typing of `fetchJsonZodProcessor` function. diff --git a/.changeset/weak-elephants-thank.md b/.changeset/weak-elephants-thank.md new file mode 100644 index 000000000..4305cfdc3 --- /dev/null +++ b/.changeset/weak-elephants-thank.md @@ -0,0 +1,5 @@ +--- +"@atproto/oauth-client-browser-example": patch +--- + +Build using SWC diff --git a/.changeset/young-parents-learn.md b/.changeset/young-parents-learn.md new file mode 100644 index 000000000..6acefadeb --- /dev/null +++ b/.changeset/young-parents-learn.md @@ -0,0 +1,5 @@ +--- +"@atproto/pds": patch +--- + +Add support for account sign-ups during OAuth flows diff --git a/.github/workflows/repo.yaml b/.github/workflows/repo.yaml index e8db4b20b..de2e2d01d 100644 --- a/.github/workflows/repo.yaml +++ b/.github/workflows/repo.yaml @@ -35,6 +35,7 @@ jobs: path: | packages/*/dist packages/*/*/dist + packages/oauth/oauth-provider/src/assets/app/locales/*/messages.ts retention-days: 1 test: name: Test diff --git a/.npmrc b/.npmrc index 8e012302a..287c23804 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ enable-pre-post-scripts = true +include-workspace-root = true diff --git a/.prettierignore b/.prettierignore index e6453c5df..570ee8a72 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,3 +12,6 @@ packages/api/src/client packages/bsky/src/lexicon packages/pds/src/lexicon packages/ozone/src/lexicon + +# Automatically generated by lingui +packages/oauth/oauth-provider/src/assets/app/locales/*/messages.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 4f8e93ded..2d95c43f7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,7 @@ "consolas", "dpop", "googleusercontent", + "hcaptcha", "hexeditor", "ingester", "insertable", diff --git a/package.json b/package.json index 2601cf849..a97bf952d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "packageManager": "pnpm@8.15.9", "scripts": { "lint:fix": "pnpm lint --fix", - "lint": "eslint . --ext .ts,.js", + "lint": "eslint . --ext .ts,.js,.tsx,.jsx", "style:fix": "prettier --write .", "style": "prettier --check .", "verify": "pnpm --stream '/^verify:.+$/'", @@ -19,13 +19,13 @@ "verify:lint": "pnpm lint", "verify:types": "tsc --build tsconfig.json", "format": "pnpm lint:fix && pnpm style:fix", - "codegen": "pnpm run --recursive --stream --filter '@atproto/lex-cli...' build --force && pnpm run --recursive --stream --parallel codegen", - "build": "pnpm --recursive --stream build", - "dev": "NODE_ENV=development pnpm --stream '/^dev:.+$/'", - "dev:tsc": "tsc --build tsconfig.json --watch", - "dev:pkg": "pnpm --recursive --parallel --stream dev", - "test": "LOG_ENABLED=false ./packages/dev-infra/with-test-redis-and-db.sh pnpm --stream -r test", - "test:withFlags": "LOG_ENABLED=false ./packages/dev-infra/with-test-redis-and-db.sh pnpm --stream -r test --", + "precodegen": "pnpm run --recursive --stream --filter '@atproto/lex-cli...' build --force", + "codegen": "pnpm run --recursive --stream --parallel codegen", + "build": "pnpm run --recursive --stream '/^(build|build:.+)$/'", + "dev": "NODE_ENV=development pnpm run --recursive --parallel --stream '/^(dev|dev:.+)$/'", + "dev:tsc": "tsc --build tsconfig.json --preserveWatchOutput --watch", + "test": "LOG_ENABLED=false ./packages/dev-infra/with-test-redis-and-db.sh pnpm test --stream --recursive", + "test:withFlags": "pnpm run test --", "changeset": "changeset", "release": "pnpm build && changeset publish", "version-packages": "changeset version && git add ." diff --git a/packages/dev-env/src/pds.ts b/packages/dev-env/src/pds.ts index d721a8797..5c2f7bd7e 100644 --- a/packages/dev-env/src/pds.ts +++ b/packages/dev-env/src/pds.ts @@ -37,7 +37,9 @@ export class TestPds { recoveryDidKey: recoveryKey, adminPassword: ADMIN_PASSWORD, jwtSecret: JWT_SECRET, - serviceHandleDomains: ['.test'], + // @NOTE ".example" will not actually work and is only used to display + // multiple domains in the sing-up UI + serviceHandleDomains: ['.test', '.example'], bskyAppViewUrl: 'https://appview.invalid', bskyAppViewDid: 'did:example:invalid', bskyAppViewCdnUrlPattern: 'http://cdn.appview.com/%s/%s/%s', @@ -47,10 +49,15 @@ export class TestPds { inviteRequired: false, disableSsrfProtection: true, serviceName: 'Development PDS', - brandColor: '#ffcb1e', - errorColor: undefined, + brandColor: '#8338ec', + errorColor: '#ff006e', + warningColor: '#fb5607', + successColor: '#02c39a', logoUrl: - 'https://uxwing.com/wp-content/themes/uxwing/download/animals-and-birds/bee-icon.png', + // Using a "data:" instead of a real URL to avoid making CORS requests in dev. + // License: https://uxwing.com/license/ + // Source: https://uxwing.com/bee-icon/ + `data:image/svg+xml;base64,${Buffer.from('bee', 'utf8').toString('base64')}`, homeUrl: 'https://bsky.social/', termsOfServiceUrl: 'https://bsky.social/about/support/tos', privacyPolicyUrl: 'https://bsky.social/about/support/privacy-policy', diff --git a/packages/internal/fetch/package.json b/packages/internal/fetch/package.json index 4c5b913a3..fbf6beb05 100644 --- a/packages/internal/fetch/package.json +++ b/packages/internal/fetch/package.json @@ -28,9 +28,6 @@ "devDependencies": { "typescript": "^5.6.3" }, - "optionalDependencies": { - "zod": "^3.23.8" - }, "scripts": { "build": "tsc --build tsconfig.json" } diff --git a/packages/internal/fetch/src/fetch-response.ts b/packages/internal/fetch/src/fetch-response.ts index 09e903bfe..d483fc819 100644 --- a/packages/internal/fetch/src/fetch-response.ts +++ b/packages/internal/fetch/src/fetch-response.ts @@ -1,4 +1,3 @@ -import type { ParseParams, TypeOf, ZodTypeAny } from 'zod' import { Transformer, pipe } from '@atproto-labs/pipe' import { FetchError } from './fetch-error.js' import { TransformedResponse } from './transformed-response.js' @@ -6,7 +5,6 @@ import { Json, MaxBytesTransformStream, cancelBody, - ifObject, ifString, logCancellationError, } from './util.js' @@ -69,15 +67,16 @@ const extractResponseMessage: ResponseMessageGetter = async (response) => { const json: unknown = await response.json() if (typeof json === 'string') return json + if (typeof json === 'object' && json != null) { + const errorDescription = ifString(json['error_description']) + if (errorDescription) return errorDescription - const errorDescription = ifString(ifObject(json)?.['error_description']) - if (errorDescription) return errorDescription + const error = ifString(json['error']) + if (error) return error - const error = ifString(ifObject(json)?.['error']) - if (error) return error - - const message = ifString(ifObject(json)?.['message']) - if (message) return message + const message = ifString(json['message']) + if (message) return message + } } } catch { // noop @@ -283,10 +282,31 @@ export function fetchJsonProcessor( ) } -export function fetchJsonZodProcessor( - schema: S, - params?: Partial, -): Transformer> { - return async (jsonResponse: ParsedJsonResponse): Promise> => - schema.parseAsync(jsonResponse.json, params) +export type SyncValidationSchema = { + parse(value: unknown, params?: P): S } + +export type AsyncValidationSchema = { + parseAsync(value: unknown, params?: P): Promise +} + +export function fetchJsonValidatorProcessor( + schema: SyncValidationSchema | AsyncValidationSchema, + params?: P, +): Transformer { + if ('parseAsync' in schema && typeof schema.parseAsync === 'function') { + return async (jsonResponse: ParsedJsonResponse): Promise => + schema.parseAsync(jsonResponse.json, params) + } + + if ('parse' in schema && typeof schema.parse === 'function') { + return async (jsonResponse: ParsedJsonResponse): Promise => + schema.parse(jsonResponse.json, params) + } + + // Needed for type safety (and allows fool proofing the usage of this function) + throw new TypeError('Invalid schema') +} + +/** @note Use {@link fetchJsonValidatorProcessor} instead */ +export const fetchJsonZodProcessor = fetchJsonValidatorProcessor diff --git a/packages/internal/fetch/src/util.ts b/packages/internal/fetch/src/util.ts index 67d5a81e2..00b64d0e3 100644 --- a/packages/internal/fetch/src/util.ts +++ b/packages/internal/fetch/src/util.ts @@ -24,24 +24,6 @@ export function isIp(hostname: string) { return false } -const plainObjectProto = Object.prototype -export const ifObject = (v: V) => { - if (typeof v === 'object' && v != null && !Array.isArray(v)) { - const proto = Object.getPrototypeOf(v) - if (proto === null || proto === plainObjectProto) { - // eslint-disable-next-line @typescript-eslint/ban-types - return v as V extends JsonScalar | JsonArray | Function | symbol - ? never - : V extends Json - ? V - : // Plain object are (mostly) safe to access using a string index - Record - } - } - - return undefined -} - export const ifString = (v: V) => (typeof v === 'string' ? v : undefined) export class MaxBytesTransformStream extends TransformStream< diff --git a/packages/internal/rollup-plugin-bundle-manifest/src/index.ts b/packages/internal/rollup-plugin-bundle-manifest/src/index.ts index dae083970..8c5623e0d 100644 --- a/packages/internal/rollup-plugin-bundle-manifest/src/index.ts +++ b/packages/internal/rollup-plugin-bundle-manifest/src/index.ts @@ -32,7 +32,7 @@ export default function bundleManifest({ }: { name?: string data?: boolean -} = {}): Plugin { +} = {}): Plugin { return { name: 'bundle-manifest', generateBundle(outputOptions, bundle) { diff --git a/packages/oauth/jwk/src/jwt.ts b/packages/oauth/jwk/src/jwt.ts index 75a84557f..264857fd3 100644 --- a/packages/oauth/jwk/src/jwt.ts +++ b/packages/oauth/jwk/src/jwt.ts @@ -129,7 +129,7 @@ export const jwtPayloadSchema = z .optional(), locale: z .string() - .regex(/^[a-z]{2}(-[A-Z]{2})?$/) + .regex(/^[a-z]{2,3}(-[A-Z]{2})?$/) .optional(), updated_at: z.number().int().optional(), diff --git a/packages/oauth/oauth-client-browser-example/package.json b/packages/oauth/oauth-client-browser-example/package.json index ba383a705..d086ebfb0 100644 --- a/packages/oauth/oauth-client-browser-example/package.json +++ b/packages/oauth/oauth-client-browser-example/package.json @@ -38,15 +38,14 @@ "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-html": "^1.0.4", "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-replace": "^5.0.5", - "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.6", - "@types/react": "^18.2.50", - "@types/react-dom": "^18.2.18", + "@rollup/plugin-swc": "^0.4.0", + "@swc/helpers": "^0.5.15", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "autoprefixer": "^10.4.17", "postcss": "^8.4.33", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "rollup": "^4.13.0", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-serve": "^1.1.1", diff --git a/packages/oauth/oauth-client-browser-example/rollup.config.js b/packages/oauth/oauth-client-browser-example/rollup.config.js index 73f561c3d..b5b79e589 100644 --- a/packages/oauth/oauth-client-browser-example/rollup.config.js +++ b/packages/oauth/oauth-client-browser-example/rollup.config.js @@ -4,9 +4,7 @@ const { default: commonjs } = require('@rollup/plugin-commonjs') const { default: html, makeHtmlAttributes } = require('@rollup/plugin-html') const { default: json } = require('@rollup/plugin-json') const { default: nodeResolve } = require('@rollup/plugin-node-resolve') -const { default: replace } = require('@rollup/plugin-replace') -const { default: terser } = require('@rollup/plugin-terser') -const { default: typescript } = require('@rollup/plugin-typescript') +const { default: swc } = require('@rollup/plugin-swc') const { defineConfig } = require('rollup') const { default: manifest, @@ -19,7 +17,7 @@ module.exports = defineConfig((commandLineArguments) => { process.env['NODE_ENV'] ?? (commandLineArguments.watch ? 'development' : 'production') - const minify = NODE_ENV !== 'development' + const devMode = NODE_ENV === 'development' return { input: 'src/main.tsx', @@ -30,17 +28,46 @@ module.exports = defineConfig((commandLineArguments) => { format: 'iife', }, plugins: [ + { + name: 'resolve-swc-helpers', + resolveId(src) { + // For some reason, "nodeResolve" doesn't resolve these: + if (src.startsWith('@swc/helpers/')) return require.resolve(src) + }, + }, nodeResolve({ preferBuiltins: false, browser: true }), commonjs(), json(), postcss({ config: true, extract: true, minimize: false }), - typescript({ - tsconfig: './tsconfig.build.json', - outputToFilesystem: true, - }), - replace({ - preventAssignment: true, - values: { 'process.env.NODE_ENV': JSON.stringify(NODE_ENV) }, + swc({ + swc: { + swcrc: false, + configFile: false, + sourceMaps: true, + minify: !devMode, + jsc: { + minify: { + compress: { + module: true, + unused: true, + }, + mangle: true, + }, + externalHelpers: true, + target: 'es2020', + parser: { syntax: 'typescript', tsx: true }, + transform: { + useDefineForClassFields: true, + react: { runtime: 'automatic' }, + optimizer: { + simplify: true, + globals: { + vars: { 'process.env.NODE_ENV': JSON.stringify(NODE_ENV) }, + }, + }, + }, + }, + }, }), html({ title: 'OAuth Client Example', @@ -79,7 +106,6 @@ module.exports = defineConfig((commandLineArguments) => { `, }), - minify && terser({}), manifest({ name: 'files.json', data: true }), commandLineArguments.watch && diff --git a/packages/oauth/oauth-client-browser-example/src/app.tsx b/packages/oauth/oauth-client-browser-example/src/app.tsx index 82e012361..77958788e 100644 --- a/packages/oauth/oauth-client-browser-example/src/app.tsx +++ b/packages/oauth/oauth-client-browser-example/src/app.tsx @@ -1,6 +1,6 @@ import { useCallback, useState } from 'react' -import { useAuthContext } from './auth/auth-provider' import { OAuthSession } from '@atproto/oauth-client' +import { useAuthContext } from './auth/auth-provider.tsx' function App() { const { pdsAgent, signOut, refresh } = useAuthContext() @@ -18,7 +18,7 @@ function App() { const [serviceAuth, setServiceAuth] = useState(undefined) const loadServiceAuth = useCallback(async () => { const serviceAuth = await pdsAgent.com.atproto.server.getServiceAuth({ - aud: pdsAgent.accountDid, + aud: pdsAgent.assertDid, }) console.log('serviceAuth', serviceAuth) setServiceAuth(serviceAuth.data) @@ -28,7 +28,7 @@ function App() { const [profile, setProfile] = useState(undefined) const loadProfile = useCallback(async () => { const profile = await pdsAgent.com.atproto.repo.getRecord({ - repo: pdsAgent.accountDid, + repo: pdsAgent.assertDid, collection: 'app.bsky.actor.profile', rkey: 'self', }) diff --git a/packages/oauth/oauth-client-browser-example/src/auth/auth-form.tsx b/packages/oauth/oauth-client-browser-example/src/auth/auth-form.tsx index acec28717..0933d6ca4 100644 --- a/packages/oauth/oauth-client-browser-example/src/auth/auth-form.tsx +++ b/packages/oauth/oauth-client-browser-example/src/auth/auth-form.tsx @@ -1,18 +1,17 @@ import { useEffect, useState } from 'react' - import { AtpSignIn, CredentialSignInForm, -} from './credential/credential-sign-in-form' -import { OAuthSignIn, OAuthSignInForm } from './oauth/oauth-sign-in-form' +} from './credential/credential-sign-in-form.tsx' +import { OAuthSignIn, OAuthSignInForm } from './oauth/oauth-sign-in-form.tsx' -export function AuthForm({ - atpSignIn, - oauthSignIn, -}: { +export type AuthFormProps = { atpSignIn?: AtpSignIn oauthSignIn?: OAuthSignIn -}) { + signUpUrl?: string +} + +export function AuthForm({ atpSignIn, oauthSignIn, signUpUrl }: AuthFormProps) { const defaultMethod = oauthSignIn ? 'oauth' : atpSignIn @@ -58,7 +57,9 @@ export function AuthForm({ - {method === 'oauth' && } + {method === 'oauth' && ( + + )} {method === 'credential' && } {method == null &&
No auth method available
} diff --git a/packages/oauth/oauth-client-browser-example/src/auth/auth-provider.tsx b/packages/oauth/oauth-client-browser-example/src/auth/auth-provider.tsx index 4ddc18ece..a97f6d695 100644 --- a/packages/oauth/oauth-client-browser-example/src/auth/auth-provider.tsx +++ b/packages/oauth/oauth-client-browser-example/src/auth/auth-provider.tsx @@ -1,26 +1,31 @@ 'use client' +import { ReactNode, createContext, useContext, useMemo } from 'react' import { Agent } from '@atproto/api' -import { createContext, ReactNode, useContext, useMemo } from 'react' +import { AuthForm } from './auth-form.tsx' +import { useCredentialAuth } from './credential/use-credential-auth.ts' +import { UseOAuthOptions, useOAuth } from './oauth/use-oauth.ts' -import { useCredentialAuth } from './credential/use-credential-auth' -import { AuthForm } from './auth-form' -import { useOAuth, UseOAuthOptions } from './oauth/use-oauth' - -export type AuthContext = { +export type AuthContextValue = { pdsAgent: Agent signOut: () => void refresh: () => void } -const AuthContext = createContext(null) +const AuthContext = createContext(null) + +export type AuthProviderProps = UseOAuthOptions & { + children: ReactNode + signUpUrl?: string +} export const AuthProvider = ({ children, + signUpUrl, + + // UseOAuthOptions ...options -}: { - children: ReactNode -} & UseOAuthOptions) => { +}: AuthProviderProps) => { const { isLoginPopup, isInitializing, @@ -38,7 +43,7 @@ export const AuthProvider = ({ refresh: credentialRefresh, } = useCredentialAuth() - const value = useMemo(() => { + const value = useMemo(() => { if (oauthAgent) { return { pdsAgent: oauthAgent, @@ -77,6 +82,7 @@ export const AuthProvider = ({ return ( ) @@ -85,7 +91,7 @@ export const AuthProvider = ({ return {children} } -export function useAuthContext(): AuthContext { +export function useAuthContext(): AuthContextValue { const context = useContext(AuthContext) if (context) return context diff --git a/packages/oauth/oauth-client-browser-example/src/auth/oauth/oauth-sign-in-form.tsx b/packages/oauth/oauth-client-browser-example/src/auth/oauth/oauth-sign-in-form.tsx index c965209ae..1f5c8cae1 100644 --- a/packages/oauth/oauth-client-browser-example/src/auth/oauth/oauth-sign-in-form.tsx +++ b/packages/oauth/oauth-client-browser-example/src/auth/oauth/oauth-sign-in-form.tsx @@ -1,59 +1,69 @@ +import { FormEvent, JSX, useState } from 'react' import { AuthorizeOptions } from '@atproto/oauth-client-browser' -import { FormEvent, useCallback, useState } from 'react' export type OAuthSignIn = (input: string, options?: AuthorizeOptions) => unknown +export type OAuthSignInFormProps = Omit< + JSX.IntrinsicElements['form'], + 'onSubmit' +> & { + signIn: OAuthSignIn + signUpUrl?: string +} + /** * @returns Nice tailwind css form asking to enter either a handle or the host * to use to login. */ export function OAuthSignInForm({ signIn, + signUpUrl, + + // form + className, ...props -}: { - signIn: OAuthSignIn -} & Omit, 'onSubmit'>) { +}: OAuthSignInFormProps) { const [value, setValue] = useState('') const [error, setError] = useState(null) const [loading, setLoading] = useState(false) - const onSubmit = useCallback( - async (e: FormEvent) => { - e.preventDefault() - if (loading) return + const onSubmit = async (event: FormEvent) => { + event.preventDefault() - setError(null) - setLoading(true) + if (loading) return + if (!event.currentTarget.reportValidity()) return - try { - if (value.startsWith('did:')) { - if (value.length > 5) await signIn(value) - else setError('DID must be at least 6 characters') - } else if ( - value.startsWith('https://') || - value.startsWith('http://') - ) { - const url = new URL(value) - if (value !== url.origin) throw new Error('PDS URL must be a origin') - await signIn(value) - } else if (value.includes('.') && value.length > 3) { - const handle = value.startsWith('@') ? value.slice(1) : value - if (handle.length > 3) await signIn(handle) - else setError('Handle must be at least 4 characters') - } + setError(null) + setLoading(true) - throw new Error('Please provide a valid handle, DID or PDS URL') - } catch (err) { - setError((err as any)?.message || String(err)) - } finally { - setLoading(false) + try { + if (value.startsWith('did:')) { + if (value.length > 5) await signIn(value) + else setError('DID must be at least 6 characters') + } else if (value.startsWith('https://') || value.startsWith('http://')) { + const url = new URL(value) + if (value !== url.origin) throw new Error('PDS URL must be a origin') + await signIn(value) + } else if (value.includes('.') && value.length > 3) { + const handle = value.startsWith('@') ? value.slice(1) : value + if (handle.length > 3) await signIn(handle) + else setError('Handle must be at least 4 characters') } - }, - [loading, value, signIn], - ) + + throw new Error('Please provide a valid handle, DID or PDS URL') + } catch (err) { + setError((err as any)?.message || String(err)) + } finally { + setLoading(false) + } + } return ( -
+
+ {signUpUrl && ( + + )} + {error ?
{error}
: null}
) diff --git a/packages/oauth/oauth-client-browser-example/src/auth/oauth/use-oauth.ts b/packages/oauth/oauth-client-browser-example/src/auth/oauth/use-oauth.ts index 9bd414f96..3d77cb8d0 100644 --- a/packages/oauth/oauth-client-browser-example/src/auth/oauth/use-oauth.ts +++ b/packages/oauth/oauth-client-browser-example/src/auth/oauth/use-oauth.ts @@ -151,7 +151,7 @@ export function useOAuth(options: UseOAuthOptions) { const [isInitializing, setIsInitializing] = useState(true) const [isLoginPopup, setIsLoginPopup] = useState(false) - const clientForInitRef = useRef() + const clientForInitRef = useRef(null) useEffect(() => { // In strict mode, we don't want to re-init() the client if it's the same if (clientForInitRef.current === clientForInit) return diff --git a/packages/oauth/oauth-client-browser-example/src/constants.ts b/packages/oauth/oauth-client-browser-example/src/constants.ts index f43772274..48962659c 100644 --- a/packages/oauth/oauth-client-browser-example/src/constants.ts +++ b/packages/oauth/oauth-client-browser-example/src/constants.ts @@ -12,3 +12,7 @@ export const PLC_DIRECTORY_URL: string | undefined = export const HANDLE_RESOLVER_URL: string = searchParams.get('handle_resolver') ?? (ENV === 'development' ? 'http://localhost:2584' : 'https://bsky.social') + +export const SIGN_UP_URL: string = + searchParams.get('sign_up_url') ?? + (ENV === 'development' ? 'http://localhost:2583' : 'https://bsky.social') diff --git a/packages/oauth/oauth-client-browser-example/src/main.tsx b/packages/oauth/oauth-client-browser-example/src/main.tsx index fdf51f138..fb837dfec 100644 --- a/packages/oauth/oauth-client-browser-example/src/main.tsx +++ b/packages/oauth/oauth-client-browser-example/src/main.tsx @@ -1,11 +1,15 @@ import './index.css' -import React from 'react' -import ReactDOM from 'react-dom/client' - -import App from './app' -import { AuthProvider } from './auth/auth-provider' -import { ENV, HANDLE_RESOLVER_URL, PLC_DIRECTORY_URL } from './constants' +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './app.tsx' +import { AuthProvider } from './auth/auth-provider.tsx' +import { + ENV, + HANDLE_RESOLVER_URL, + PLC_DIRECTORY_URL, + SIGN_UP_URL, +} from './constants.ts' const clientId = `http://localhost?${new URLSearchParams({ scope: 'atproto transition:generic', @@ -14,20 +18,22 @@ const clientId = `http://localhost?${new URLSearchParams({ search: new URLSearchParams({ env: ENV, handle_resolver: HANDLE_RESOLVER_URL, + sign_up_url: SIGN_UP_URL, ...(PLC_DIRECTORY_URL && { plc_directory_url: PLC_DIRECTORY_URL }), }).toString(), }).href, })}` -ReactDOM.createRoot(document.getElementById('root')!).render( - +createRoot(document.getElementById('root')!).render( + - , + , ) diff --git a/packages/oauth/oauth-client-browser-example/tailwind.config.js b/packages/oauth/oauth-client-browser-example/tailwind.config.js index aea8d354b..50ce09436 100644 --- a/packages/oauth/oauth-client-browser-example/tailwind.config.js +++ b/packages/oauth/oauth-client-browser-example/tailwind.config.js @@ -5,4 +5,6 @@ export default { extend: {}, }, plugins: [], + // See rollup.config.js for classes used in the HTML template + safelist: ['bg-white', 'dark:bg-slate-800'], } diff --git a/packages/oauth/oauth-client/src/oauth-response-error.ts b/packages/oauth/oauth-client/src/oauth-response-error.ts index 5c08a0f4d..de6d17b19 100644 --- a/packages/oauth/oauth-client/src/oauth-response-error.ts +++ b/packages/oauth/oauth-client/src/oauth-response-error.ts @@ -1,4 +1,5 @@ -import { Json, ifObject, ifString } from '@atproto-labs/fetch' +import { Json } from '@atproto-labs/fetch' +import { ifString } from './util.js' export class OAuthResponseError extends Error { readonly error?: string @@ -8,8 +9,9 @@ export class OAuthResponseError extends Error { public readonly response: Response, public readonly payload: Json, ) { - const error = ifString(ifObject(payload)?.['error']) - const errorDescription = ifString(ifObject(payload)?.['error_description']) + const objPayload = typeof payload === 'object' ? payload : undefined + const error = ifString(objPayload?.['error']) + const errorDescription = ifString(objPayload?.['error_description']) const messageError = error ? `"${error}"` : 'unknown' const messageDesc = errorDescription ? `: ${errorDescription}` : '' diff --git a/packages/oauth/oauth-client/src/util.ts b/packages/oauth/oauth-client/src/util.ts index eef60c186..051c19a6e 100644 --- a/packages/oauth/oauth-client/src/util.ts +++ b/packages/oauth/oauth-client/src/util.ts @@ -4,6 +4,8 @@ export type Simplify = { [K in keyof T]: T[K] } & NonNullable // @ts-expect-error Symbol.dispose ??= Symbol('@@dispose') +export const ifString = (v: V) => (typeof v === 'string' ? v : undefined) + /** * @todo (?) move to common package */ diff --git a/packages/oauth/oauth-provider/.gitignore b/packages/oauth/oauth-provider/.gitignore new file mode 100644 index 000000000..200f0ce2a --- /dev/null +++ b/packages/oauth/oauth-provider/.gitignore @@ -0,0 +1,2 @@ +src/assets/app/locales/*/*.ts +.swc diff --git a/packages/oauth/oauth-provider/.linguirc b/packages/oauth/oauth-provider/.linguirc new file mode 100644 index 000000000..800b873e8 --- /dev/null +++ b/packages/oauth/oauth-provider/.linguirc @@ -0,0 +1,57 @@ +{ + "format": "po", + "sourceLocale": "en", + "locales": [ + "en", + "an", + "ast", + "ca", + "da", + "de", + "el", + "en-GB", + "es", + "eu", + "fi", + "fr", + "ga", + "gl", + "hi", + "hu", + "ia", + "id", + "it", + "ja", + "km", + "ko", + "ne", + "nl", + "pl", + "pt-BR", + "ro", + "ru", + "sv", + "th", + "tr", + "uk", + "vi", + "zh-CN", + "zh-HK", + "zh-TW" + ], + "fallbackLocales": { + "default": "en" + }, + "catalogs": [ + { + "path": "/src/assets/app/locales/{locale}/messages", + "include": [ + "/src/assets/app" + ], + "exclude": [ + "**/dist/**", + "**/node_modules/**" + ] + } + ] +} diff --git a/packages/oauth/oauth-provider/.postcssrc.yml b/packages/oauth/oauth-provider/.postcssrc.yml deleted file mode 100644 index 0114fbc9e..000000000 --- a/packages/oauth/oauth-provider/.postcssrc.yml +++ /dev/null @@ -1,3 +0,0 @@ -plugins: - tailwindcss: {} - autoprefixer: {} diff --git a/packages/oauth/oauth-provider/package.json b/packages/oauth/oauth-provider/package.json index 9f43d55d2..fa831454d 100644 --- a/packages/oauth/oauth-provider/package.json +++ b/packages/oauth/oauth-provider/package.json @@ -41,9 +41,11 @@ "@atproto/jwk-jose": "workspace:*", "@atproto/oauth-types": "workspace:*", "@hapi/accept": "^6.0.3", + "@hapi/address": "^5.1.1", "@hapi/bourne": "^3.0.0", "@hapi/content": "^6.0.0", "cookie": "^0.6.0", + "disposable-email-domains-js": "^1.5.0", "forwarded": "^0.2.0", "http-errors": "^2.0.0", "ioredis": "^5.3.2", @@ -53,31 +55,52 @@ }, "devDependencies": { "@atproto-labs/rollup-plugin-bundle-manifest": "workspace:*", - "@rollup/plugin-commonjs": "^25.0.7", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-replace": "^5.0.5", - "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.6", + "@hcaptcha/react-hcaptcha": "^1.11.2", + "@lingui/cli": "^5.2.0", + "@lingui/core": "^5.2.0", + "@lingui/react": "^5.2.0", + "@lingui/swc-plugin": "^5.4.0", + "@lingui/vite-plugin": "^5.2.0", + "@rollup/plugin-commonjs": "^28.0.2", + "@rollup/plugin-dynamic-import-vars": "^2.1.5", + "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-swc": "^0.4.0", + "@swc/core": "^1.10.18", + "@swc/helpers": "^0.5.15", "@types/cookie": "^0.6.0", "@types/forwarded": "0.1.3", "@types/psl": "1.1.3", - "@types/react": "^18.2.50", - "@types/react-dom": "^18.2.18", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "@types/send": "^0.17.4", + "@vitejs/plugin-react-swc": "^3.8.0", "@web/rollup-plugin-import-meta-assets": "^2.2.1", "autoprefixer": "^10.4.17", - "postcss": "^8.4.33", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "postcss": "^8.4.38", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-error-boundary": "^5.0.0", "rollup": "^4.13.0", "rollup-plugin-postcss": "^4.0.2", - "tailwindcss": "^3.4.1", - "typescript": "^5.6.3" + "tailwindcss": "^3.4.3", + "typescript": "^5.6.3", + "vite": "^6.2.0" }, "scripts": { + "po:extract": "lingui extract --clean", + "po:compile": "lingui compile --typescript", + "prebuild:frontend": "pnpm po:compile", "build:frontend": "rollup --config rollup.config.js", "build:backend": "tsc --build --force tsconfig.backend.json", - "build": "pnpm --parallel --stream '/^build:.+$/'", - "dev": "rollup --config rollup.config.js --watch" + "start:ui": "vite", + "dev:frontend": "pnpm run build:frontend --watch", + "dev:catalogs": "pnpm run po:extract --debounce 250 --watch > /dev/null", + "dev:messages": "pnpm run po:compile --debounce 500 --watch" + }, + "postcss": { + "plugins": { + "tailwindcss": {}, + "autoprefixer": {} + } } } diff --git a/packages/oauth/oauth-provider/rollup.config.js b/packages/oauth/oauth-provider/rollup.config.js index 7ff599c3a..c1b25d213 100644 --- a/packages/oauth/oauth-provider/rollup.config.js +++ b/packages/oauth/oauth-provider/rollup.config.js @@ -1,10 +1,11 @@ /* eslint-env node */ const { default: commonjs } = require('@rollup/plugin-commonjs') +const { + default: dynamicImportVars, +} = require('@rollup/plugin-dynamic-import-vars') const { default: nodeResolve } = require('@rollup/plugin-node-resolve') -const { default: replace } = require('@rollup/plugin-replace') -const { default: terser } = require('@rollup/plugin-terser') -const { default: typescript } = require('@rollup/plugin-typescript') +const { default: swc } = require('@rollup/plugin-swc') const { defineConfig } = require('rollup') const { default: manifest, @@ -16,34 +17,77 @@ module.exports = defineConfig((commandLineArguments) => { process.env['NODE_ENV'] ?? (commandLineArguments.watch ? 'development' : 'production') - const minify = NODE_ENV !== 'development' + const devMode = NODE_ENV === 'development' return { - input: 'src/assets/app/main.tsx', + input: ['src/assets/app/main.tsx', 'src/assets/app/main.css'], output: { manualChunks: undefined, sourcemap: true, - file: 'dist/assets/app/main.js', - format: 'iife', + dir: 'dist/assets/app', + format: 'module', + entryFileNames: 'main-[hash].js', }, plugins: [ - nodeResolve({ preferBuiltins: false, browser: true }), + { + name: 'resolve-swc-helpers', + resolveId(src) { + // For some reason, "nodeResolve" doesn't resolve these: + if (src.startsWith('@swc/helpers/')) return require.resolve(src) + }, + }, + nodeResolve({ + preferBuiltins: false, + browser: true, + exportConditions: ['browser', 'module', 'import', 'default'], + }), commonjs(), - postcss({ config: true, extract: true, minimize: minify }), - typescript({ - tsconfig: './tsconfig.frontend.json', - outputToFilesystem: true, - }), - replace({ - preventAssignment: true, - values: { 'process.env.NODE_ENV': JSON.stringify(NODE_ENV) }, + postcss({ config: true, extract: true, minimize: !devMode }), + swc({ + swc: { + swcrc: false, + configFile: false, + sourceMaps: true, + minify: !devMode, + jsc: { + experimental: { + // @NOTE Because of the experimental nature of SWC plugins, A + // very particular version of @swc/core needs to be used. The + // link below allows to determine with version of @swc/core is + // compatible based on the version of @lingui/swc-plugin used + // (click on the swc_core version in the right column to see + // which version of the @swc/core is compatible) + // + // https://github.com/lingui/swc-plugin?tab=readme-ov-file#compatibility + plugins: [['@lingui/swc-plugin', {}]], + }, + minify: { + compress: true, + mangle: true, + }, + externalHelpers: true, + target: 'es2020', + parser: { syntax: 'typescript', tsx: true }, + transform: { + useDefineForClassFields: true, + react: { runtime: 'automatic' }, + optimizer: { + simplify: true, + globals: { + vars: { 'process.env.NODE_ENV': JSON.stringify(NODE_ENV) }, + }, + }, + }, + }, + }, }), + dynamicImportVars({ errorWhenNoFilesFound: true }), + // Change `data` to `true` to include assets data in the manifest, // allowing for easier bundling of the backend code (eg. using esbuild) as // bundlers know how to bundle JSON files but not how to bundle assets // referenced at runtime. manifest({ data: false }), - minify && terser({}), ], onwarn(warning, warn) { // 'use client' directives are fine diff --git a/packages/oauth/oauth-provider/src/account/account-manager.ts b/packages/oauth/oauth-provider/src/account/account-manager.ts index 64a8b8c1b..1abc3d05e 100644 --- a/packages/oauth/oauth-provider/src/account/account-manager.ts +++ b/packages/oauth/oauth-provider/src/account/account-manager.ts @@ -1,31 +1,164 @@ -import { isOAuthClientIdLoopback } from '@atproto/oauth-types' +import { + OAuthIssuerIdentifier, + isOAuthClientIdLoopback, +} from '@atproto/oauth-types' import { Client } from '../client/client.js' import { DeviceId } from '../device/device-id.js' +import { InvalidRequestError } from '../errors/invalid-request-error.js' +import { HCaptchaClient, HcaptchaVerifyResult } from '../lib/hcaptcha.js' +import { callAsync } from '../lib/util/function.js' import { constantTime } from '../lib/util/time.js' -import { InvalidRequestError } from '../oauth-errors.js' +import { OAuthHooks, RequestMetadata } from '../oauth-hooks.js' +import { Customization } from '../oauth-provider.js' import { Sub } from '../oidc/sub.js' import { ClientAuth } from '../token/token-store.js' import { Account, AccountInfo, AccountStore, - SignInCredentials, + ResetPasswordConfirmData, + ResetPasswordRequestData, } from './account-store.js' +import { SignInData } from './sign-in-data.js' +import { SignUpData } from './sign-up-data.js' const TIMING_ATTACK_MITIGATION_DELAY = 400 +const BRUTE_FORCE_MITIGATION_DELAY = 300 export class AccountManager { - constructor(protected readonly store: AccountStore) {} + protected readonly inviteCodeRequired: boolean + protected readonly hcaptchaClient?: HCaptchaClient + + constructor( + issuer: OAuthIssuerIdentifier, + protected readonly store: AccountStore, + protected readonly hooks: OAuthHooks, + customization: Customization, + ) { + this.inviteCodeRequired = customization.inviteCodeRequired !== false + this.hcaptchaClient = customization.hcaptcha + ? new HCaptchaClient(new URL(issuer).hostname, customization.hcaptcha) + : undefined + } + + protected async verifySignupData( + data: SignUpData, + deviceId: DeviceId, + deviceMetadata: RequestMetadata, + ): Promise { + let hcaptchaResult: undefined | HcaptchaVerifyResult + + if (this.inviteCodeRequired && !data.inviteCode) { + throw new InvalidRequestError('Invite code is required') + } + + if (this.hcaptchaClient) { + if (!data.hcaptchaToken) { + throw new InvalidRequestError('hCaptcha token is required') + } + + const { allowed, result } = await this.hcaptchaClient.verify( + 'signup', + data.hcaptchaToken, + deviceMetadata.ipAddress, + data.handle, + deviceMetadata.userAgent, + ) + + await callAsync(this.hooks.onSignupHcaptchaResult, { + data, + allowed, + result, + deviceId, + deviceMetadata, + }) + + if (!allowed) { + throw new InvalidRequestError('hCaptcha verification failed') + } + + hcaptchaResult = result + } + + await callAsync(this.hooks.onSignupAttempt, { + data, + deviceId, + deviceMetadata, + hcaptchaResult, + }) + } + + public async signUp( + data: SignUpData, + deviceId: DeviceId, + deviceMetadata: RequestMetadata, + ): Promise { + await this.verifySignupData(data, deviceId, deviceMetadata) + + // Mitigation against brute forcing email of users. + // @TODO Add rate limit to all the OAuth routes. + return constantTime(BRUTE_FORCE_MITIGATION_DELAY, async () => { + let account: Account + try { + account = await this.store.createAccount(data) + } catch (err) { + throw InvalidRequestError.from(err, 'Account creation failed') + } + + try { + const info = await this.store.addDeviceAccount( + deviceId, + account.sub, + false, + ) + + await callAsync(this.hooks.onSignedUp, { + data, + info, + account, + deviceId, + deviceMetadata, + }) + + return { account, info } + } catch (err) { + throw InvalidRequestError.from( + err, + 'Something went wrong, try singing-in', + ) + } + }) + } public async signIn( - credentials: SignInCredentials, + data: SignInData, deviceId: DeviceId, + deviceMetadata: RequestMetadata, ): Promise { return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => { - const result = await this.store.authenticateAccount(credentials, deviceId) - if (result) return result + try { + const account = await this.store.authenticateAccount(data) + const info = await this.store.addDeviceAccount( + deviceId, + account.sub, + data.remember, + ) - throw new InvalidRequestError('Invalid credentials') + await callAsync(this.hooks.onSignedIn, { + data, + info, + account, + deviceId, + deviceMetadata, + }) + + return { account, info } + } catch (err) { + throw InvalidRequestError.from( + err, + 'Unable to sign-in due to an unexpected server error', + ) + } }) } @@ -52,4 +185,22 @@ export class AccountManager { const results = await this.store.listDeviceAccounts(deviceId) return results.filter((result) => result.info.remembered) } + + public async resetPasswordRequest(data: ResetPasswordRequestData) { + return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => { + await this.store.resetPasswordRequest(data) + }) + } + + public async resetPasswordConfirm(data: ResetPasswordConfirmData) { + return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => { + await this.store.resetPasswordConfirm(data) + }) + } + + public async verifyHandleAvailability(handle: string): Promise { + return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => { + return this.store.verifyHandleAvailability(handle) + }) + } } diff --git a/packages/oauth/oauth-provider/src/account/account-store.ts b/packages/oauth/oauth-provider/src/account/account-store.ts index ad49f8d98..9e7037f2c 100644 --- a/packages/oauth/oauth-provider/src/account/account-store.ts +++ b/packages/oauth/oauth-provider/src/account/account-store.ts @@ -1,25 +1,86 @@ +import { isEmailValid } from '@hapi/address' +import { isDisposableEmail } from 'disposable-email-domains-js' import { z } from 'zod' import { ClientId } from '../client/client-id.js' import { DeviceId } from '../device/device-id.js' -import { Awaitable } from '../lib/util/type.js' +import { localeSchema } from '../lib/locale.js' +import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js' +import { + HandleUnavailableError, + InvalidRequestError, + SecondAuthenticationFactorRequiredError, +} from '../oauth-errors.js' import { Sub } from '../oidc/sub.js' import { Account } from './account.js' -export const signInCredentialsSchema = z.object({ - username: z.string(), - password: z.string(), +// @NOTE Change the length here to force stronger passwords (through a reset) +export const oldPasswordSchema = z.string().min(1) +export const newPasswordSchema = z.string().min(8) +export const tokenSchema = z.string().regex(/^[A-Z2-7]{5}-[A-Z2-7]{5}$/) +export const handleSchema = z + .string() + .min(3) + .max(30) + .regex(/^[a-z0-9][a-z0-9-]+[a-z0-9](?:\.[a-z0-9][a-z0-9-]+[a-z0-9])+$/) +export const emailSchema = z + .string() + .email() + // @NOTE using @hapi/address here, in addition to the email() check to ensure + // compatibility with the current email validation in the PDS's account + // manager + .refine(isEmailValid, { + message: 'Invalid email address', + }) + .refine((email) => !isDisposableEmail(email), { + message: 'Disposable email addresses are not allowed', + }) - /** - * If false, the account must not be returned from - * {@link AccountStore.listDeviceAccounts}. Note that this only makes sense when - * used with a device ID. - */ - remember: z.boolean().optional().default(false), +export const authenticateAccountDataSchema = z + .object({ + locale: localeSchema, + username: z.string(), + password: oldPasswordSchema, + emailOtp: z.string().optional(), + }) + .strict() - emailOtp: z.string().optional(), -}) +export type AuthenticateAccountData = z.TypeOf< + typeof authenticateAccountDataSchema +> -export type SignInCredentials = z.TypeOf +export const createAccountDataSchema = z + .object({ + locale: localeSchema, + handle: handleSchema, + email: emailSchema, + password: z.intersection(oldPasswordSchema, newPasswordSchema), + inviteCode: tokenSchema.optional(), + }) + .strict() + +export type CreateAccountData = z.TypeOf + +export const resetPasswordRequestDataSchema = z + .object({ + locale: localeSchema, + email: emailSchema, + }) + .strict() + +export type ResetPasswordRequestData = z.TypeOf< + typeof resetPasswordRequestDataSchema +> + +export const resetPasswordConfirmDataSchema = z + .object({ + token: tokenSchema, + password: z.intersection(oldPasswordSchema, newPasswordSchema), + }) + .strict() + +export type ResetPasswordConfirmData = z.TypeOf< + typeof resetPasswordConfirmDataSchema +> export type DeviceAccountInfo = { remembered: boolean @@ -28,7 +89,14 @@ export type DeviceAccountInfo = { } // Export all types needed to implement the AccountStore interface -export type { Account, DeviceId, Sub } +export { + type Account, + type DeviceId, + HandleUnavailableError, + InvalidRequestError, + SecondAuthenticationFactorRequiredError, + type Sub, +} export type AccountInfo = { account: Account @@ -36,10 +104,17 @@ export type AccountInfo = { } export interface AccountStore { - authenticateAccount( - credentials: SignInCredentials, - deviceId: DeviceId, - ): Awaitable + /** + * @throws {HandleUnavailableError} - To indicate that the handle is already taken + * @throws {InvalidRequestError} - To indicate that some data is invalid + */ + createAccount(data: CreateAccountData): Awaitable + + /** + * @throws {InvalidRequestError} - When the credentials are not valid + * @throws {SecondAuthenticationFactorRequiredError} - To indicate that an {@link SecondAuthenticationFactorRequiredError.type} is required in the credentials + */ + authenticateAccount(data: AuthenticateAccountData): Awaitable addAuthorizedClient( deviceId: DeviceId, @@ -47,6 +122,19 @@ export interface AccountStore { clientId: ClientId, ): Awaitable + /** + * @param remember If false, the account must not be returned from + * {@link AccountStore.listDeviceAccounts}. + */ + addDeviceAccount( + deviceId: DeviceId, + sub: Sub, + remember: boolean, + ): Awaitable + + /** + * @returns The account info, whether the account, even if remember was false. + */ getDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable removeDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable @@ -55,23 +143,30 @@ export interface AccountStore { * be returned. The others will be ignored. */ listDeviceAccounts(deviceId: DeviceId): Awaitable + + resetPasswordRequest(data: ResetPasswordRequestData): Awaitable + resetPasswordConfirm(data: ResetPasswordConfirmData): Awaitable + + /** + * @throws {HandleUnavailableError} - To indicate that the handle is already taken + */ + verifyHandleAvailability(handle: string): Awaitable } -export function isAccountStore( - implementation: Record & Partial, -): implementation is Record & AccountStore { - return ( - typeof implementation.authenticateAccount === 'function' && - typeof implementation.getDeviceAccount === 'function' && - typeof implementation.addAuthorizedClient === 'function' && - typeof implementation.listDeviceAccounts === 'function' && - typeof implementation.removeDeviceAccount === 'function' - ) -} +export const isAccountStore = buildInterfaceChecker([ + 'createAccount', + 'authenticateAccount', + 'addAuthorizedClient', + 'addDeviceAccount', + 'getDeviceAccount', + 'removeDeviceAccount', + 'listDeviceAccounts', + 'resetPasswordRequest', + 'resetPasswordConfirm', + 'verifyHandleAvailability', +]) -export function asAccountStore( - implementation?: Record & Partial, -): AccountStore { +export function asAccountStore(implementation: V): V & AccountStore { if (!implementation || !isAccountStore(implementation)) { throw new Error('Invalid AccountStore implementation') } diff --git a/packages/oauth/oauth-provider/src/account/sign-in-data.ts b/packages/oauth/oauth-provider/src/account/sign-in-data.ts new file mode 100644 index 000000000..47a742a3a --- /dev/null +++ b/packages/oauth/oauth-provider/src/account/sign-in-data.ts @@ -0,0 +1,15 @@ +import { z } from 'zod' +import { authenticateAccountDataSchema } from './account-store.js' + +export const signInDataSchema = authenticateAccountDataSchema + .extend({ + /** + * If false, the account must not be returned from + * {@link AccountStore.listDeviceAccounts}. Note that this only makes sense when + * used with a device ID. + */ + remember: z.boolean().optional().default(false), + }) + .strict() + +export type SignInData = z.TypeOf diff --git a/packages/oauth/oauth-provider/src/account/sign-up-data.ts b/packages/oauth/oauth-provider/src/account/sign-up-data.ts new file mode 100644 index 000000000..cf9aac8ab --- /dev/null +++ b/packages/oauth/oauth-provider/src/account/sign-up-data.ts @@ -0,0 +1,11 @@ +import { z } from 'zod' +import { hcaptchaTokenSchema } from '../lib/hcaptcha.js' +import { createAccountDataSchema } from './account-store.js' + +export const signUpDataSchema = createAccountDataSchema + .extend({ + hcaptchaToken: hcaptchaTokenSchema.optional(), + }) + .strict() + +export type SignUpData = z.TypeOf diff --git a/packages/oauth/oauth-provider/src/assets/app/app.tsx b/packages/oauth/oauth-provider/src/assets/app/app.tsx index e925ffae5..000ab3300 100644 --- a/packages/oauth/oauth-provider/src/assets/app/app.tsx +++ b/packages/oauth/oauth-provider/src/assets/app/app.tsx @@ -1,28 +1,43 @@ +import { ErrorBoundary } from 'react-error-boundary' import type { AuthorizeData, + AvailableLocales, CustomizationData, ErrorData, -} from './backend-data' -import { AuthorizeView } from './views/authorize-view' -import { ErrorView } from './views/error-view' +} from './backend-types.ts' +import { LocaleProvider } from './locales/locale-provider.tsx' +import { AuthorizeView } from './views/authorize/authorize-view.tsx' +import { ErrorView } from './views/error/error-view.tsx' export type AppProps = { + availableLocales?: AvailableLocales authorizeData?: AuthorizeData customizationData?: CustomizationData errorData?: ErrorData } -export function App({ authorizeData, customizationData, errorData }: AppProps) { - if (authorizeData && !errorData) { - return ( - - ) - } else { - return ( - - ) - } +export function App({ + availableLocales, + authorizeData, + customizationData, + errorData, +}: AppProps) { + return ( + + ( + + )} + > + {errorData || !authorizeData ? ( + + ) : ( + + )} + + + ) } diff --git a/packages/oauth/oauth-provider/src/assets/app/backend-data.ts b/packages/oauth/oauth-provider/src/assets/app/backend-data.ts index ae9f48748..0884246d4 100644 --- a/packages/oauth/oauth-provider/src/assets/app/backend-data.ts +++ b/packages/oauth/oauth-provider/src/assets/app/backend-data.ts @@ -1,72 +1,27 @@ -import { OAuthClientMetadata } from '@atproto/oauth-types' +import { + AuthorizeData, + AvailableLocales, + CustomizationData, + ErrorData, +} from './backend-types.ts' -// TODO: Find a way to share these types with the backend code - -export type Account = { - sub: string - aud: string - - email?: string - name?: string - preferred_username?: string - picture?: string -} - -export type Session = { - account: Account - info?: never // Prevent relying on this in the frontend - - selected: boolean - loginRequired: boolean - consentRequired: boolean -} - -export type LinkDefinition = { - title: string - href: string - rel?: string -} - -export type CustomizationData = { - name?: string - logo?: string - links?: LinkDefinition[] -} - -export type ErrorData = { - error: string - error_description: string -} - -export type ScopeDetail = { - scope: string - description?: string -} - -export type AuthorizeData = { - clientId: string - clientMetadata: OAuthClientMetadata - clientTrusted: boolean - requestUri: string - csrfCookie: string - loginHint?: string - scopeDetails?: ScopeDetail[] - newSessionsRequireConsent: boolean - sessions: Session[] -} - -// see "declareBackendData()" in the backend -const readBackendData = (key: string): T | undefined => { +function readBackendData(key: string): T | undefined { const value = window[key] as T | undefined delete window[key] // Prevent accidental usage / potential leaks to dependencies return value } // These values are injected by the backend when it builds the -// page HTML. +// page HTML. See "declareBackendData()" in the backend. +/** @deprecated Do not import directly. Only import this from main.tsx */ +export const availableLocales = + readBackendData('__availableLocales') +/** @deprecated Do not import directly. Only import this from main.tsx */ export const customizationData = readBackendData( '__customizationData', ) +/** @deprecated Do not import directly. Only import this from main.tsx */ export const errorData = readBackendData('__errorData') +/** @deprecated Do not import directly. Only import this from main.tsx */ export const authorizeData = readBackendData('__authorizeData') diff --git a/packages/oauth/oauth-provider/src/assets/app/backend-types.ts b/packages/oauth/oauth-provider/src/assets/app/backend-types.ts new file mode 100644 index 000000000..212bc2349 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/backend-types.ts @@ -0,0 +1,66 @@ +import type { OAuthClientMetadata } from '@atproto/oauth-types' + +// @TODO: Find a way to share these types with the backend code + +export type Account = { + sub: string + aud: string | [string, ...string[]] + + email?: string + email_verified?: boolean + name?: string + preferred_username?: string + picture?: string +} + +export type Session = { + account: Account + info?: never // Prevent relying on this in the frontend + + selected: boolean + loginRequired: boolean + consentRequired: boolean +} + +export type LocalizedString = string | ({ en: string } & Record) + +export type AvailableLocales = readonly string[] + +export type LinkDefinition = { + title: LocalizedString + href: string + rel?: string +} + +export type CustomizationData = { + // Functional customization + hcaptchaSiteKey?: string + inviteCodeRequired?: boolean + availableUserDomains?: string[] + + // Aesthetic customization + name?: string + logo?: string + links?: LinkDefinition[] +} + +export type ErrorData = { + error: string + error_description: string +} + +export type ScopeDetail = { + scope: string + description?: string +} + +export type AuthorizeData = { + clientId: string + clientMetadata: OAuthClientMetadata + clientTrusted: boolean + requestUri: string + loginHint?: string + scopeDetails?: ScopeDetail[] + newSessionsRequireConsent: boolean + sessions: Session[] +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/accept-form.tsx b/packages/oauth/oauth-provider/src/assets/app/components/accept-form.tsx deleted file mode 100644 index 27c2a1aec..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/accept-form.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { OAuthClientMetadata } from '@atproto/oauth-types' -import { FormEvent } from 'react' - -import { Account, ScopeDetail } from '../backend-data' -import { Override } from '../lib/util' -import { AccountIdentifier } from './account-identifier' -import { Button } from './button' -import { ClientName } from './client-name' -import { FormCard, FormCardProps } from './form-card' - -export type AcceptFormProps = Override< - FormCardProps, - { - clientId: string - clientMetadata: OAuthClientMetadata - clientTrusted: boolean - - account: Account - scopeDetails?: ScopeDetail[] - - onAccept: () => void - acceptLabel?: string - - onReject: () => void - rejectLabel?: string - - onBack?: () => void - backLabel?: string - } -> - -export function AcceptForm({ - clientId, - clientMetadata, - clientTrusted, - - account, - scopeDetails, - - onAccept, - acceptLabel = 'Accept', - onReject, - rejectLabel = 'Deny access', - onBack, - backLabel = 'Back', - - ...props -}: AcceptFormProps) { - const doSubmit = (e: FormEvent) => { - e.preventDefault() - onAccept() - } - - return ( - {backLabel}} - actions={ - <> - - - - - } - {...props} - > - {clientTrusted && clientMetadata.logo_uri && ( -
- {clientMetadata.client_name} -
- )} -

- {' '} - is asking for permission to access your account ( - - ). -

- -

- By clicking {acceptLabel}, you allow this application to perform - the following actions in accordance to their{' '} - - terms of service - - {' and '} - - privacy policy - - : -

- - {scopeDetails?.length ? ( -
    - {scopeDetails.map( - ({ scope, description = getScopeDescription(scope) }) => ( -
  • {description}
  • - ), - )} -
- ) : null} -
- ) -} - -function getScopeDescription(scope: string): string { - switch (scope) { - case 'atproto': - return 'Uniquely identify you' - case 'transition:generic': - return 'Access your account data (except chat messages)' - case 'transition:chat.bsky': - return 'Access your chat messages' - default: - return scope - } -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/account-identifier.tsx b/packages/oauth/oauth-provider/src/assets/app/components/account-identifier.tsx deleted file mode 100644 index 96b860e32..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/account-identifier.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { HTMLAttributes } from 'react' - -import { Account } from '../backend-data' - -export type AccountIdentifierProps = { - account: Account -} - -export function AccountIdentifier({ - account, - ...attrs -}: AccountIdentifierProps & HTMLAttributes) { - return ( - - {account.preferred_username || account.email || account.sub} - - ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/account-picker.tsx b/packages/oauth/oauth-provider/src/assets/app/components/account-picker.tsx deleted file mode 100644 index 93b195941..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/account-picker.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import type { ReactNode } from 'react' -import { Account } from '../backend-data' -import { Override } from '../lib/util' -import { Button } from './button' -import { FormCard, FormCardProps } from './form-card' -import { AtSymbolIcon } from './icons/at-symbol-icon' -import { CaretRightIcon } from './icons/caret-right-icon' -import { InputContainer } from './input-container' -import { Fieldset } from './fieldset' - -export type AccountPickerProps = Override< - FormCardProps, - { - accounts: readonly Account[] - - onAccount: (account: Account) => void - accountAria?: (account: Account) => string - - onOther?: () => void - otherLabel?: ReactNode - otherAria?: string - - onBack?: () => void - backLabel?: ReactNode - backAria?: string - } -> - -export function AccountPicker({ - accounts, - - onAccount, - accountAria = (a) => `Sign in as ${a.name}`, - - onOther = undefined, - otherLabel = 'Another account', - otherAria = 'Login to account that is not listed', - - onBack, - backAria, - backLabel = backAria, - - ...props -}: AccountPickerProps) { - return ( - - {backLabel} - - ) - } - > -
- {accounts.map((account) => { - const [name, identifier] = [ - account.name, - account.preferred_username, - account.email, - account.sub, - ].filter(Boolean) as [string, string?] - - return ( - onAccount(account)} - role="button" - aria-label={accountAria(account)} - icon={ - account.picture ? ( - {name} - ) : ( - - - - - - ) - } - append={} - > - - {name} - {identifier && ( - - {identifier} - - )} - - - ) - })} - - {onOther && ( - } - icon={} - > - - {otherLabel} - - - )} -
-
- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/button.tsx b/packages/oauth/oauth-provider/src/assets/app/components/button.tsx deleted file mode 100644 index 5900c7241..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/button.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { ButtonHTMLAttributes } from 'react' -import { clsx } from '../lib/clsx' - -export function Button({ - children, - className, - type = 'button', - role = 'Button', - color = 'grey', - disabled = false, - loading = undefined, - ...props -}: { - color?: 'brand' | 'grey' - loading?: boolean -} & ButtonHTMLAttributes) { - return ( - - ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/client-name.tsx b/packages/oauth/oauth-provider/src/assets/app/components/client-name.tsx deleted file mode 100644 index 98244d4d2..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/client-name.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { - isOAuthClientIdDiscoverable, - isOAuthClientIdLoopback, - OAuthClientMetadata, -} from '@atproto/oauth-types' -import { HTMLAttributes } from 'react' - -import { UrlViewer } from './url-viewer' - -export type ClientNameProps = { - clientId: string - clientMetadata: OAuthClientMetadata - clientTrusted: boolean - loopbackClientName?: string -} & HTMLAttributes - -export function ClientName({ - clientId, - clientMetadata, - clientTrusted, - loopbackClientName = 'An application on your device', - ...attrs -}: ClientNameProps) { - if (clientTrusted && clientMetadata.client_name) { - return {clientMetadata.client_name} - } - - if (isOAuthClientIdLoopback(clientId)) { - return {loopbackClientName} - } - - if (isOAuthClientIdDiscoverable(clientId)) { - return - } - - return {clientId} -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/fieldset.tsx b/packages/oauth/oauth-provider/src/assets/app/components/fieldset.tsx deleted file mode 100644 index 4185246c9..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/fieldset.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { FieldsetHTMLAttributes, forwardRef, ReactNode } from 'react' -import { Override } from '../lib/util' - -export type FieldsetCardProps = Override< - FieldsetHTMLAttributes, - { - title?: ReactNode - } -> - -export const Fieldset = forwardRef( - ({ title, children, ...props }, ref) => ( -
- {title && ( -

- {title} -

- )} - -
{children}
-
- ), -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/form-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/form-card.tsx deleted file mode 100644 index 029e98678..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/form-card.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { FormHTMLAttributes, forwardRef, ReactNode } from 'react' -import { InfoCard } from './info-card' -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' - -export type FormCardProps = Override< - FormHTMLAttributes, - { - append?: ReactNode - error?: ReactNode - cancel?: ReactNode - actions?: ReactNode - } -> - -export const FormCard = forwardRef( - ({ actions, cancel, append, className, children, error, ...props }, ref) => { - return ( -
-
{children}
- - {append &&
{append}
} - - {error && ( - - {error} - - )} - - {(actions || cancel) && ( -
- {actions} -
- {cancel} -
- )} - - ) - }, -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/button-toggle-visibility.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/button-toggle-visibility.tsx new file mode 100644 index 000000000..7b35a2216 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/button-toggle-visibility.tsx @@ -0,0 +1,43 @@ +import { useLingui } from '@lingui/react/macro' +import { Override } from '../../lib/util.ts' +import { EyeIcon, EyeSlashIcon } from '../utils/icons.tsx' +import { Button, ButtonProps } from './button.tsx' + +export type ButtonToggleVisibilityProps = Override< + Omit, + { + visible: boolean + toggleVisible: () => void + } +> + +/** + * Generic button to toggle visibility of an item (e.g. password). + */ +export function ButtonToggleVisibility({ + visible, + toggleVisible, + + // button + onClick, + ...props +}: ButtonToggleVisibilityProps) { + const { t } = useLingui() + return ( + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/button.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/button.tsx new file mode 100644 index 000000000..5e5f5626d --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/button.tsx @@ -0,0 +1,60 @@ +import { JSX } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' + +export type ButtonProps = Override< + JSX.IntrinsicElements['button'], + { + color?: 'brand' | 'grey' + loading?: boolean + transparent?: boolean + square?: boolean + } +> + +export function Button({ + color = 'grey', + transparent = false, + loading = undefined, + square = false, + + // button + children, + className, + type = 'button', + role = 'Button', + disabled = false, + ...props +}: ButtonProps) { + return ( + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/fieldset.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/fieldset.tsx new file mode 100644 index 000000000..d8b83fea8 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/fieldset.tsx @@ -0,0 +1,55 @@ +import { JSX, ReactNode, createContext, useMemo } from 'react' +import { useRandomString } from '../../hooks/use-random-string.ts' +import { Override } from '../../lib/util.ts' + +export type FieldsetContextValue = { + disabled: boolean + labelId?: string +} + +export const FieldsetContext = createContext({ + disabled: false, +}) +FieldsetContext.displayName = 'FieldsetContext' + +export type FieldsetCardProps = Override< + Omit, + { + label?: ReactNode + } +> + +export function Fieldset({ + label, + children, + disabled, + ...props +}: FieldsetCardProps) { + const labelId = useRandomString({ prefix: 'fieldset-' }) + + const contextValue = useMemo( + () => ({ + disabled: disabled ?? false, + labelId: label ? labelId : undefined, + }), + [disabled, label, labelId], + ) + + return ( +
+ {label && ( + + {label} + + )} + +
+ {children} +
+
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card-async.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card-async.tsx new file mode 100644 index 000000000..387054d68 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card-async.tsx @@ -0,0 +1,103 @@ +import { Trans } from '@lingui/react/macro' +import { FormEvent, ReactNode, useCallback } from 'react' +import { + UseAsyncActionOptions, + useAsyncAction, +} from '../../hooks/use-async-action.ts' +import { Override } from '../../lib/util.ts' +import { ErrorCard } from '../utils/error-card.tsx' +import { Button } from './button.tsx' +import { FormCard, FormCardProps } from './form-card.tsx' + +export type { AsyncActionController } from '../../hooks/use-async-action.ts' + +export type ErrorRender = (data: { error: Error }) => ReactNode +export const errorRenderDefault: ErrorRender = ({ error }) => ( + +) + +export type FormCardAsyncProps = Override< + Override< + Omit, + Pick + >, + { + invalid?: boolean + disabled?: boolean + + onSubmit: (signal: AbortSignal) => void | PromiseLike + submitLabel?: ReactNode + + onCancel?: () => void + cancelLabel?: ReactNode + + errorRender?: ErrorRender + } +> + +export function FormCardAsync({ + invalid, + disabled, + + onSubmit, + submitLabel, + + onCancel = undefined, + cancelLabel, + + errorRender = errorRenderDefault, + + // UseAsyncActionOptions + ref, + onLoading, + onError, + + // FormCardProps + children, + ...props +}: FormCardAsyncProps) { + const { run, loading, error } = useAsyncAction(onSubmit, { + ref, + onError, + onLoading, + }) + + const doSubmit = useCallback( + (event: FormEvent) => { + event.preventDefault() + + if (!event.currentTarget.reportValidity()) return + + if (!disabled && !invalid) void run() + }, + [disabled, invalid, run], + ) + + return ( + + {cancelLabel || Cancel} + + ) + } + actions={ + + } + > + {children} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card.tsx new file mode 100644 index 000000000..6ef610507 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card.tsx @@ -0,0 +1,49 @@ +import { JSX, ReactNode } from 'react' +import { Override } from '../../lib/util.ts' + +export type FormCardProps = Override< + JSX.IntrinsicElements['form'], + { + disabled?: boolean + append?: ReactNode + prepend?: ReactNode + cancel?: ReactNode + actions?: ReactNode + } +> + +export function FormCard({ + actions, + cancel, + append, + children, + prepend, + disabled, + + // form + inert = disabled, + ...props +}: FormCardProps) { + return ( +
+ {prepend &&
{prepend}
} + +
+ {children} +
+ + {append &&
{append}
} + + {(actions || cancel) && ( +
+ {actions} +
+ {cancel} +
+ )} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-checkbox.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-checkbox.tsx new file mode 100644 index 000000000..006d05fa4 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-checkbox.tsx @@ -0,0 +1,73 @@ +import { JSX, ReactNode, useContext, useRef } from 'react' +import { useRandomString } from '../../hooks/use-random-string.ts' +import { clsx } from '../../lib/clsx.ts' +import { mergeRefs } from '../../lib/ref.ts' +import { Override } from '../../lib/util.ts' +import { FieldsetContext } from './fieldset.tsx' +import { InputContainer } from './input-container.tsx' + +export type InputCheckboxProps = Override< + Omit, + { + className?: string + children?: ReactNode + } +> + +export function InputCheckbox({ + className, + children, + + // input + id, + ref, + disabled, + 'aria-labelledby': ariaLabelledBy, + ...props +}: InputCheckboxProps) { + const htmlFor = useRandomString('input-checkbox-') + const containerRef = useRef(null) + const inputRef = useRef(null) + const ctx = useContext(FieldsetContext) + + const inputId = id ?? htmlFor + + return ( + " element (through "htmlFor") over the wrapping "
" to describe the checkbox. + undefined + : ariaLabelledBy ?? ctx.labelId + } + ref={mergeRefs([ref, inputRef])} + id={inputId} + className="accent-brand outline-none" + type="checkbox" + /> + } + tabIndex={-1} + onClick={(event) => { + if (event.target === containerRef.current && !event.defaultPrevented) { + inputRef.current?.click() + inputRef.current?.focus() + } + }} + > + {children && ( + + )} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-container.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-container.tsx new file mode 100644 index 000000000..b1e92abd3 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-container.tsx @@ -0,0 +1,107 @@ +import { JSX, ReactNode, useState } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' + +export type InputContainerProps = Override< + JSX.IntrinsicElements['div'], + { + icon: ReactNode + append?: ReactNode + bellow?: ReactNode + } +> + +export function InputContainer({ + icon, + append, + bellow, + + // div + className, + children, + onFocus, + onBlur, + ...props +}: InputContainerProps) { + const [hasFocus, setHasFocus] = useState(false) + + return ( +
{ + onFocus?.(event) + if (!event.defaultPrevented) setHasFocus(true) + }} + onBlur={(event) => { + onBlur?.(event) + if (!event.defaultPrevented) setHasFocus(false) + }} + className={clsx( + // Layout + 'min-h-12', + 'max-w-full', + 'overflow-hidden', + // Border + 'rounded-lg', + className, + )} + > +
+ {icon && ( +
+ {icon} +
+ )} + + {children} + +
{append}
+
+ {bellow && ( +
+ {bellow} +
+ )} +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-email-address.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-email-address.tsx new file mode 100644 index 000000000..ced2b5002 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-email-address.tsx @@ -0,0 +1,66 @@ +import { useLingui } from '@lingui/react/macro' +import { ChangeEvent, useCallback, useState } from 'react' +import { Override } from '../../lib/util.ts' +import { AtSymbolIcon } from '../utils/icons.tsx' +import { InputText, InputTextProps } from './input-text.tsx' + +export type InputEmailAddressProps = Override< + Omit, + { + onEmail?: (email: string | undefined) => void + } +> + +export function InputEmailAddress({ + onEmail, + + // InputTextProps + autoCapitalize = 'none', + autoComplete = 'email', + autoCorrect = 'off', + dir = 'auto', + icon = , + onBlur, + onChange, + pattern = '^[^@]+@[^@]+\\.[^@]+$', + spellCheck = 'false', + value, + defaultValue = value, + ...props +}: InputEmailAddressProps) { + const { t } = useLingui() + const [email, setEmail] = useState( + typeof defaultValue === 'string' ? defaultValue : '', + ) + + const doChange = useCallback( + (event: ChangeEvent) => { + const email = event.target.value.toLowerCase() + + setEmail(email) + onChange?.(event) + onEmail?.(event.target.validity.valid ? email : undefined) + }, + [onChange, onEmail], + ) + + return ( + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-new-password.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-new-password.tsx new file mode 100644 index 000000000..ec84e36d5 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-new-password.tsx @@ -0,0 +1,62 @@ +import { useLingui } from '@lingui/react/macro' +import { ChangeEvent, useCallback, useState } from 'react' +import { MIN_PASSWORD_LENGTH } from '../../lib/password.ts' +import { Override } from '../../lib/util.ts' +import { PasswordStrengthLabel } from '../utils/password-strength-label.tsx' +import { PasswordStrengthMeter } from '../utils/password-strength-meter.tsx' +import { InputPassword, InputPasswordProps } from './input-password.tsx' + +export type InputNewPasswordProps = Override< + Omit, + { + password?: string + onPassword?: (password: undefined | string) => void + } +> + +export function InputNewPassword({ + password: passwordInit = '', + onPassword, + + // InputPasswordProps + onChange, + autoComplete = 'new-password', + minLength = MIN_PASSWORD_LENGTH, + ...props +}: InputNewPasswordProps) { + const { t } = useLingui() + const [password, setPassword] = useState(passwordInit) + + const doChange = useCallback( + (event: ChangeEvent) => { + const { value } = event.target + onChange?.(event) + if (event.defaultPrevented) return + setPassword(value) + onPassword?.(event.target.validity.valid ? value : undefined) + }, + [onChange, onPassword], + ) + + return ( + + + + + } + /> + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-password.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-password.tsx new file mode 100644 index 000000000..d98cd06e5 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-password.tsx @@ -0,0 +1,88 @@ +import { useLingui } from '@lingui/react/macro' +import { ChangeEvent, useCallback, useRef, useState } from 'react' +import { mergeRefs } from '../../lib/ref.ts' +import { Override } from '../../lib/util.ts' +import { LockIcon } from '../utils/icons.tsx' +import { ButtonToggleVisibility } from './button-toggle-visibility.tsx' +import { InputText, InputTextProps } from './input-text.tsx' + +export type InputPasswordProps = Override< + Omit, + { + autoHide?: boolean + } +> + +export function InputPassword({ + autoHide = true, + + // InputTextProps + onBlur, + onChange, + append, + autoComplete = 'current-password', + icon = , + value, + defaultValue = value, + ref, + dir = 'auto', + autoCapitalize = 'none', + autoCorrect = 'off', + spellCheck = 'false', + ...props +}: InputPasswordProps) { + const { t } = useLingui() + const inputRef = useRef(null) + const [visible, setVisible] = useState(false) + const [password, setPassword] = useState( + typeof defaultValue === 'string' ? defaultValue : '', + ) + + const doChange = useCallback( + (event: ChangeEvent) => { + onChange?.(event) + setPassword(event.target.value) + }, + [onChange], + ) + + return ( + { + onBlur?.(event) + if (!event.defaultPrevented) setVisible(false) + } + : onBlur + } + value={password} + onChange={doChange} + type={visible ? 'text' : 'password'} + autoComplete={autoComplete} + append={ + <> + { + setVisible((prev) => !prev) + inputRef.current?.focus() + }} + /> + {append} + + } + /> + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-text.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-text.tsx new file mode 100644 index 000000000..f093dc0ae --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-text.tsx @@ -0,0 +1,76 @@ +import { JSX, ReactNode, useContext, useRef } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { mergeRefs } from '../../lib/ref.ts' +import { Override } from '../../lib/util.ts' +import { FieldsetContext } from './fieldset.tsx' +import { InputContainer } from './input-container.tsx' + +export type InputTextProps = Override< + Omit, + { + icon?: ReactNode + append?: ReactNode + bellow?: ReactNode + className?: string + } +> + +export function InputText({ + icon, + append, + bellow, + className, + + // input + onFocus, + onBlur, + ref, + disabled, + 'aria-labelledby': ariaLabelledBy, + ...props +}: InputTextProps) { + const ctx = useContext(FieldsetContext) + + const inputRef = useRef(null) + const focusedRef = useRef(false) // ref instead of state to avoid re-renders + + return ( + { + if (inputRef.current !== event.target) { + event.preventDefault() + event.stopPropagation() + inputRef.current?.focus() + } + }} + onMouseDown={(event) => { + if (focusedRef.current && event.target !== inputRef.current) { + // Prevent "blur" event from firing when clicking outside the input + event.preventDefault() + event.stopPropagation() + } + }} + > + { + onFocus?.(event) + if (!event.defaultPrevented) focusedRef.current = true + }} + onBlur={(event) => { + onBlur?.(event) + if (!event.defaultPrevented) focusedRef.current = false + }} + /> + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-token.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-token.tsx new file mode 100644 index 000000000..78f6702c7 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-token.tsx @@ -0,0 +1,94 @@ +import { useLingui } from '@lingui/react/macro' +import { ChangeEvent, useState } from 'react' +import { Override } from '../../lib/util.ts' +import { TokenIcon } from '../utils/icons.tsx' +import { InputText, InputTextProps } from './input-text.tsx' + +export type InputTokenProps = Override< + Omit< + InputTextProps, + | 'type' + | 'pattern' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoComplete' + | 'spellCheck' + | 'minLength' + | 'maxLength' + | 'placeholder' + | 'dir' + >, + { + example?: string + onToken?: (code: string | null) => void + } +> + +export const OTP_CODE_EXAMPLE = 'XXXXX-XXXXX' + +export function InputToken({ + example = OTP_CODE_EXAMPLE, + onToken, + + // InputTextProps + icon = , + title = example, + onChange, + value, + defaultValue = value, + ...props +}: InputTokenProps) { + const { t } = useLingui() + const [token, setToken] = useState( + typeof defaultValue === 'string' ? defaultValue : '', + ) + + return ( + ) => { + const { value, selectionEnd, selectionStart } = event.currentTarget + + const fixedValue = fix(value) + + event.currentTarget.value = fixedValue + + // Move the cursor back where it was relative to the original value + const pos = selectionEnd ?? selectionStart + if (pos != null) { + const fixedSlicedValue = fix(value.slice(0, pos)) + event.currentTarget.selectionStart = + event.currentTarget.selectionEnd = fixedSlicedValue.length + } + + setToken(fixedValue) + onChange?.(event) + + if (!event.isDefaultPrevented()) { + onToken?.(fixedValue.length === 11 ? fixedValue : null) + } + }} + /> + ) +} + +function fix(value: string) { + const normalized = value.toUpperCase().replaceAll(/[^A-Z2-7]/g, '') + + if (normalized.length <= 5) return normalized + + return `${normalized.slice(0, 5)}-${normalized.slice(5, 10)}` +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/wizard-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/wizard-card.tsx new file mode 100644 index 000000000..3ed6727ca --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/wizard-card.tsx @@ -0,0 +1,116 @@ +import { Trans } from '@lingui/react/macro' +import { JSX, ReactNode, useCallback } from 'react' +import { DisabledStep, Step, useStepper } from '../../hooks/use-stepper.ts' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' + +export type DoneFn = (...a: any) => unknown + +export type WizardRenderProps = { + /** + * Indicates wether the render function being invoked corresponds to the step + * currently active. The steps titles could, for example, be rendered in a + * list of links, where the current step is highlighted (based on `current`). + * + * Another use for this is to render the next/previous steps in order to + * provide animated transitions between steps. In this case, `current` would + * be used to disable any form interaction with the form transitioning in/out. + */ + current: boolean + invalid: boolean + + prev?: () => void + prevLabel: ReactNode + + // On the last step, the "next()" function will actually be the done function + next: (() => void) | TDone + nextLabel: ReactNode +} + +export type WizardRenderFn = ( + data: WizardRenderProps, +) => ReactNode + +export type WizardStep = Step & { + titleRender?: WizardRenderFn + contentRender: WizardRenderFn +} + +export type WizardCardProps = Override< + Omit, + { + prevLabel?: ReactNode + nextLabel?: ReactNode + + onBack?: () => void + backLabel?: ReactNode + + onDone: TDone + doneLabel?: ReactNode + + steps: readonly (WizardStep | DisabledStep)[] + } +> + +export function WizardCard({ + prevLabel, + nextLabel, + + onBack, + backLabel, + + onDone, + doneLabel, + + steps, + className, + + ...props +}: WizardCardProps) { + const { + atFirst, + atLast, + count, + current, + currentPosition, + completed, + toNext, + toPrev, + toRequired, + } = useStepper(steps) + + // Memoized to avoid re-renders in child (rendered) components + const onNext = useCallback(() => { + // If already at last step, go to the first incomplete (required) step + if (!toNext()) toRequired() + }, [toNext, toRequired]) + + const data: WizardRenderProps = { + // The current UI only displays the current title & content. + current: true, + invalid: current ? current.invalid : false, + + prevLabel: (atFirst && backLabel) || prevLabel || Back, + prev: atFirst ? onBack : toPrev, + + nextLabel: (atLast && doneLabel) || nextLabel || Next, + next: atLast && completed ? onDone : onNext, + } + + const stepTitle = current?.titleRender?.(data) + const stepContent = current?.contentRender?.(data) + + return ( +
+

+ + Step {currentPosition} of {count} + +

+ + {stepTitle &&

{stepTitle}

} + + {stepContent} +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/help-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/help-card.tsx deleted file mode 100644 index f71b6f47e..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/help-card.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { HTMLAttributes } from 'react' -import { LinkDefinition } from '../backend-data' -import { clsx } from '../lib/clsx' - -export type HelpCardProps = { - links?: readonly LinkDefinition[] -} - -export function HelpCard({ - links, - - className, - ...attrs -}: HelpCardProps & - Omit< - HTMLAttributes, - keyof HelpCardProps | 'children' - >) { - const helpLink = links?.find((l) => l.rel === 'help') - - if (!helpLink) return null - - return ( -

- Having trouble?{' '} - - Contact {helpLink.title} - -

- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/alert-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/alert-icon.tsx deleted file mode 100644 index ee3c840b5..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/alert-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const AlertIcon = makeSvgComponent( - 'M11.14 4.494a.995.995 0 0 1 1.72 0l7.001 12.008a.996.996 0 0 1-.86 1.498H4.999a.996.996 0 0 1-.86-1.498L11.14 4.494Zm3.447-1.007c-1.155-1.983-4.019-1.983-5.174 0L2.41 15.494C1.247 17.491 2.686 20 4.998 20h14.004c2.312 0 3.751-2.509 2.587-4.506L14.587 3.487ZM13 9.019a1 1 0 1 0-2 0v2.994a1 1 0 1 0 2 0V9.02Zm-1 4.731a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/at-symbol-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/at-symbol-icon.tsx deleted file mode 100644 index 5410dbe7a..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/at-symbol-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const AtSymbolIcon = makeSvgComponent( - 'M12 4a8 8 0 1 0 4.21 14.804 1 1 0 0 1 1.054 1.7A9.958 9.958 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 1.104-.27 2.31-.949 3.243-.716.984-1.849 1.6-3.331 1.465a4.207 4.207 0 0 1-2.93-1.585c-.94 1.21-2.388 1.94-3.985 1.715-2.53-.356-4.04-2.91-3.682-5.458.358-2.547 2.514-4.586 5.044-4.23.905.127 1.68.536 2.286 1.126a1 1 0 0 1 1.964.368l-.515 3.545v.002a2.222 2.222 0 0 0 1.999 2.526c.75.068 1.212-.21 1.533-.65.358-.493.566-1.245.566-2.067a8 8 0 0 0-8-8Zm-.112 5.13c-1.195-.168-2.544.819-2.784 2.529-.24 1.71.784 3.03 1.98 3.198 1.195.168 2.543-.819 2.784-2.529.24-1.71-.784-3.03-1.98-3.198Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/caret-right-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/caret-right-icon.tsx deleted file mode 100644 index fe8b62e80..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/caret-right-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const CaretRightIcon = makeSvgComponent( - 'M8.293 3.293a1 1 0 0 1 1.414 0l8 8a1 1 0 0 1 0 1.414l-8 8a1 1 0 0 1-1.414-1.414L15.586 12 8.293 4.707a1 1 0 0 1 0-1.414Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/lock-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/lock-icon.tsx deleted file mode 100644 index 135ecc9d9..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/lock-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const LockIcon = makeSvgComponent( - 'M7 7a5 5 0 0 1 10 0v2h1a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h1V7Zm-1 4v9h12v-9H6Zm9-2H9V7a3 3 0 1 1 6 0v2Zm-3 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0v-3a1 1 0 0 1 1-1Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/token-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/token-icon.tsx deleted file mode 100644 index b7f57c0e3..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/token-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const TokenIcon = makeSvgComponent( - 'M4 5.5a.5.5 0 0 0-.5.5v2.535a.5.5 0 0 0 .25.433A3.498 3.498 0 0 1 5.5 12a3.498 3.498 0 0 1-1.75 3.032.5.5 0 0 0-.25.433V18a.5.5 0 0 0 .5.5h16a.5.5 0 0 0 .5-.5v-2.535a.5.5 0 0 0-.25-.433A3.498 3.498 0 0 1 18.5 12a3.5 3.5 0 0 1 1.75-3.032.5.5 0 0 0 .25-.433V6a.5.5 0 0 0-.5-.5H4ZM2.5 6A1.5 1.5 0 0 1 4 4.5h16A1.5 1.5 0 0 1 21.5 6v3.17a.5.5 0 0 1-.333.472 2.501 2.501 0 0 0 0 4.716.5.5 0 0 1 .333.471V18a1.5 1.5 0 0 1-1.5 1.5H4A1.5 1.5 0 0 1 2.5 18v-3.17a.5.5 0 0 1 .333-.472 2.501 2.501 0 0 0 0-4.716.5.5 0 0 1-.333-.471V6Zm12 2a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/util.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/util.tsx deleted file mode 100644 index 9ef11cde1..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/util.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { SVGProps } from 'react' - -export const makeSvgComponent = (path: string) => - function ( - props: Omit, 'viewBox' | 'children' | 'xmlns'>, - ) { - return ( - - - - ) - } diff --git a/packages/oauth/oauth-provider/src/assets/app/components/info-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/info-card.tsx deleted file mode 100644 index 69c2fa6af..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/info-card.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' -import { AlertIcon } from './icons/alert-icon' -import { InputLayout, InputLayoutProps } from './input-layout' - -export type InfoCardProps = Override< - InputLayoutProps, - { - role: 'alert' | 'status' - } -> - -export function InfoCard({ - children, - className, - role = 'alert', - ...props -}: InfoCardProps) { - return ( - - } - {...props} - > -
- {children} -
-
- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/input-checkbox.tsx b/packages/oauth/oauth-provider/src/assets/app/components/input-checkbox.tsx deleted file mode 100644 index 3bda701ff..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/input-checkbox.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { InputHTMLAttributes, useRef, useState } from 'react' -import { InputContainer } from './input-container' - -const generateUniqueId = () => Math.random().toString(36).slice(2) - -export type InputCheckboxProps = Omit< - InputHTMLAttributes, - 'type' -> - -export function InputCheckbox({ - id, - children, - className, - ...props -}: InputCheckboxProps) { - const [htmlFor] = useState(generateUniqueId) - const ref = useRef(null) - const inputRef = useRef(null) - - return ( - - } - className={className} - onClick={(event) => { - if (event.target === ref.current && !event.defaultPrevented) { - inputRef.current?.click() - inputRef.current?.focus() - } - }} - > - - - ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/input-container.tsx b/packages/oauth/oauth-provider/src/assets/app/components/input-container.tsx deleted file mode 100644 index dc4165260..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/input-container.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { forwardRef, useState } from 'react' -import { clsx } from '../lib/clsx' -import { InputLayout, InputLayoutProps } from './input-layout' - -export type InputContainerProps = InputLayoutProps - -export const InputContainer = forwardRef( - ({ className, onFocus, icon, onBlur, ...props }, ref) => { - const [focused, setFocused] = useState(false) - - return ( - { - onFocus?.(event) - if (!event.defaultPrevented) setFocused(true) - }} - onBlur={(event) => { - onBlur?.(event) - if (!event.defaultPrevented) setFocused(false) - }} - icon={
{icon}
} - {...props} - /> - ) - }, -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/input-layout.tsx b/packages/oauth/oauth-provider/src/assets/app/components/input-layout.tsx deleted file mode 100644 index 686f0ab79..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/input-layout.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { forwardRef, HTMLAttributes, ReactNode } from 'react' -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' - -export type InputLayoutProps = Override< - HTMLAttributes, - { - icon?: ReactNode - append?: ReactNode - } -> - -export const InputLayout = forwardRef( - ({ className, icon, append, children, ...props }, ref) => { - return ( -
-
- {icon} -
-
{children}
- {append &&
{append}
} -
- ) - }, -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/input-text.tsx b/packages/oauth/oauth-provider/src/assets/app/components/input-text.tsx deleted file mode 100644 index fea570f9e..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/input-text.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { - forwardRef, - InputHTMLAttributes, - MouseEventHandler, - ReactNode, - useCallback, - useImperativeHandle, - useRef, - useState, -} from 'react' -import { InputContainer } from './input-container' - -export const InputText = forwardRef< - HTMLInputElement, - { - icon?: ReactNode - } & InputHTMLAttributes ->(({ className, icon, children, onFocus, onBlur, ...props }, ref) => { - const [focused, setFocused] = useState(false) - const inputRef = useRef(null) - - useImperativeHandle(ref, () => inputRef.current!, []) - - const handleClick = useCallback>( - (event) => { - if (inputRef.current !== event.target) { - event.preventDefault() - event.stopPropagation() - inputRef.current?.focus() - } - }, - [], - ) - - const handleMouseDown = useCallback>( - (event) => { - if (focused && event.target !== inputRef.current) { - // Prevent "blur" event from firing when clicking outside the input - event.preventDefault() - event.stopPropagation() - } - }, - [focused], - ) - - return ( - - { - setFocused(true) - onFocus?.(event) - }} - onBlur={(event) => { - setFocused(false) - onBlur?.(event) - }} - {...props} - /> - {children} - - ) -}) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/layout-title-page.tsx b/packages/oauth/oauth-provider/src/assets/app/components/layout-title-page.tsx deleted file mode 100644 index a68b597d4..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/layout-title-page.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { HTMLAttributes, ReactNode } from 'react' -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' - -export type LayoutTitlePageProps = Override< - HTMLAttributes, - { - title?: ReactNode - subtitle?: ReactNode - } -> - -export function LayoutTitlePage({ - children, - title, - subtitle, - className, - ...props -}: LayoutTitlePageProps) { - return ( -
-
- {title && ( -

- {title} -

- )} - - {subtitle && ( -

- {subtitle} -

- )} -
- -
{children}
-
- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/layout-welcome.tsx b/packages/oauth/oauth-provider/src/assets/app/components/layout-welcome.tsx deleted file mode 100644 index 0e1f8189e..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/layout-welcome.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { HTMLAttributes } from 'react' -import { Override } from '../lib/util' -import { clsx } from '../lib/clsx' - -export type LayoutWelcomeProps = Override< - HTMLAttributes, - { - name?: string - logo?: string - links?: Array<{ - title: string - href: string - rel?: string - }> - logoAlt?: string - } -> - -export function LayoutWelcome({ - name, - logo, - logoAlt = name || 'Logo', - links, - children, - className, - ...props -}: LayoutWelcomeProps) { - return ( -
-
- {logo && ( - {logoAlt} - )} - - {name && ( -

- {name} -

- )} - - {children} -
- - {links != null && links.length > 0 && ( - - )} -
- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-title-page.tsx b/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-title-page.tsx new file mode 100644 index 000000000..f612cc265 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-title-page.tsx @@ -0,0 +1,77 @@ +import { JSX, ReactNode } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' +import { LocaleSelector } from '../../locales/locale-selector.tsx' + +export type LayoutTitlePageProps = Override< + JSX.IntrinsicElements['div'], + { + title?: string + subtitle?: ReactNode + children?: ReactNode + } +> + +export function LayoutTitlePage({ + children, + title, + subtitle, + + // HTMLDivElement + className, + ...props +}: LayoutTitlePageProps) { + return ( +
+ {title && {title}} + +
+
+ {title && ( +

+ {title} +

+ )} + + {subtitle && ( +

+ {subtitle} +

+ )} +
+ + +
+ +
{children}
+
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-welcome.tsx b/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-welcome.tsx new file mode 100644 index 000000000..a4e8175ae --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-welcome.tsx @@ -0,0 +1,73 @@ +import { JSX } from 'react' +import { CustomizationData } from '../../backend-types.ts' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' +import { LocaleSelector } from '../../locales/locale-selector.tsx' +import { LinkAnchor } from '../utils/link-anchor.tsx' + +export type LayoutWelcomeProps = Override< + JSX.IntrinsicElements['div'], + { + customizationData: CustomizationData | undefined + title?: string + } +> + +export function LayoutWelcome({ + customizationData: { logo, name, links } = {}, + title = name, + + // div + className, + children, + ...props +}: LayoutWelcomeProps) { + return ( +
+ {title && {title}} + +
+ {logo && ( + {name + )} + + {name && ( +

+ {name} +

+ )} + + {children} +
+ + +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/sign-in-form.tsx b/packages/oauth/oauth-provider/src/assets/app/components/sign-in-form.tsx deleted file mode 100644 index ff30bb1ac..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/sign-in-form.tsx +++ /dev/null @@ -1,337 +0,0 @@ -import { ReactNode, SyntheticEvent, useCallback, useState } from 'react' - -import { - InvalidCredentialsError, - SecondAuthenticationFactorRequiredError, -} from '../lib/api' -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' -import { Button } from './button' -import { FormCard, FormCardProps } from './form-card' -import { AtSymbolIcon } from './icons/at-symbol-icon' -import { LockIcon } from './icons/lock-icon' -import { InfoCard } from './info-card' -import { InputCheckbox } from './input-checkbox' -import { InputText } from './input-text' -import { TokenIcon } from './icons/token-icon' -import { Fieldset } from './fieldset' - -export type SignInFormOutput = { - username: string - password: string - remember?: boolean -} - -export type SignInFormProps = Override< - FormCardProps, - { - onSubmit: (credentials: SignInFormOutput) => void | PromiseLike - submitLabel?: ReactNode - submitAria?: string - - onCancel?: () => void - cancelLabel?: ReactNode - cancelAria?: string - - accountSection?: ReactNode - sessionSection?: ReactNode - secondFactorSection?: ReactNode - - usernameDefault?: string - usernameReadonly?: boolean - usernameLabel?: string - usernamePlaceholder?: string - usernameAria?: string - usernamePattern?: string - usernameFormat?: string - - passwordLabel?: string - passwordPlaceholder?: string - passwordWarning?: ReactNode - passwordAria?: string - passwordPattern?: string - passwordFormat?: string - - secondFactorLabel?: string - secondFactorPlaceholder?: string - secondFactorAria?: string - secondFactorPattern?: string - secondFactorFormat?: string - secondFactorHint?: string - secondFactorParseValue?: (value: string) => string | false - - rememberVisible?: boolean - rememberDefault?: boolean - rememberLabel?: string - rememberAria?: string - } -> - -export function SignInForm({ - onSubmit, - submitAria = 'Next', - submitLabel = submitAria, - - onCancel = undefined, - cancelAria = 'Cancel', - cancelLabel = cancelAria, - - accountSection = 'Account', - sessionSection = 'Session', - secondFactorSection = '2FA Confirmation', - - usernameDefault = '', - usernameReadonly = false, - usernameLabel = 'Username or email address', - usernameAria = usernameLabel, - usernamePlaceholder = usernameLabel, - usernamePattern = undefined, - usernameFormat = 'valid email address or username', - - passwordLabel = 'Password', - passwordAria = passwordLabel, - passwordPlaceholder = passwordLabel, - passwordPattern = undefined, - passwordFormat = 'non empty string', - passwordWarning = ( - <> -

Warning

-

- Please verify the domain name of the website before entering your - password. Never enter your password on a domain you do not trust. -

- - ), - - secondFactorLabel = 'Confirmation code', - secondFactorAria = secondFactorLabel, - secondFactorPlaceholder = secondFactorLabel, - secondFactorPattern = '^[A-Z2-7]{5}-[A-Z2-7]{5}$', - secondFactorFormat = 'XXXXX-XXXXX', - secondFactorHint = 'Check your $1 email for a login code and enter it here.', - secondFactorParseValue = checkAndFormatEmailOtpCode, - - rememberVisible = true, - rememberDefault = false, - rememberLabel = 'Remember this account on this device', - rememberAria = rememberLabel, - - ...props -}: SignInFormProps) { - const [focused, setFocused] = useState(false) - const [loading, setLoading] = useState(false) - const [secondFactor, setSecondFactor] = useState(null) - - const [errorMessage, setErrorMessage] = useState(null) - - const resetState = useCallback(() => { - setSecondFactor(null) - setErrorMessage(null) - }, []) - - const passwordReadonly = secondFactor != null - - const doSubmit = useCallback( - async ( - event: SyntheticEvent< - HTMLFormElement & { - username: HTMLInputElement - password: HTMLInputElement - remember?: HTMLInputElement - secondFactor?: HTMLInputElement - }, - SubmitEvent - >, - ) => { - event.preventDefault() - - const credentials: SignInFormOutput = { - username: event.currentTarget.username.value, - password: event.currentTarget.password.value, - remember: event.currentTarget.remember?.checked, - } - - if (secondFactor) { - const element = event.currentTarget.secondFactor - if (!element) throw new Error('Second factor input not found') - const value = secondFactorParseValue(element.value) - if (!value) { - setSecondFactor({ - type: secondFactor.type, - hint: `Make sure to match the format: ${secondFactorFormat}`, - }) - return - } - credentials[secondFactor.type] = value - } - - setLoading(true) - setErrorMessage(null) - try { - await onSubmit(credentials) - } catch (err) { - if (err instanceof SecondAuthenticationFactorRequiredError) { - setSecondFactor({ - type: err.type, - hint: err.hint, - }) - } else { - setErrorMessage(parseErrorMessage(err)) - } - } finally { - setLoading(false) - } - }, - [secondFactor, onSubmit], - ) - - return ( - - {cancelLabel} - - ) - } - actions={ - - } - {...props} - > -
- } - name="username" - type="text" - onChange={resetState} - placeholder={usernamePlaceholder} - aria-label={usernameAria} - autoCapitalize="none" - autoCorrect="off" - autoComplete="username" - spellCheck="false" - dir="auto" - enterKeyHint="next" - required - defaultValue={usernameDefault} - readOnly={usernameReadonly} - disabled={usernameReadonly} - pattern={usernamePattern} - title={usernameFormat} - /> - - } - name="password" - type="password" - onChange={resetState} - onFocus={() => setFocused(true)} - onBlur={() => setTimeout(setFocused, 100, false)} - placeholder={passwordPlaceholder} - aria-label={passwordAria} - autoCapitalize="none" - autoCorrect="off" - autoComplete="current-password" - dir="auto" - enterKeyHint="done" - spellCheck="false" - required - readOnly={passwordReadonly} - disabled={passwordReadonly} - pattern={passwordPattern} - title={passwordFormat} - /> - - {passwordWarning && ( -
- {passwordWarning} -
- )} -
- - {rememberVisible && ( -
- - {rememberLabel} - -
- )} - - {secondFactor && ( -
-
- } - name="secondFactor" - type="text" - placeholder={secondFactorPlaceholder} - aria-label={secondFactorAria} - autoCapitalize="none" - autoCorrect="off" - autoComplete="off" - spellCheck="false" - dir="auto" - enterKeyHint="done" - required - pattern={secondFactorPattern} - title={secondFactorFormat} - autoFocus={true} - /> -

- {secondFactorHint.replaceAll('$1', secondFactor.hint)} -

-
-
- )} -
- ) -} - -function parseErrorMessage(err: unknown): string { - if (err instanceof InvalidCredentialsError) { - return 'Invalid username or password' - } - - return 'An unknown error occurred' -} - -export function checkAndFormatEmailOtpCode(code: string): string | false { - const EMAIL_CODE_REGEX = /^[A-Z2-7]{5}-[A-Z2-7]{5}$/ - - // Trim the reset code - let fixed = code.trim().toUpperCase() - - // Add a dash if needed - if (fixed.length === 10) { - fixed = `${fixed.slice(0, 5)}-${fixed.slice(5, 10)}` - } - - // Check that it is a valid format - if (!EMAIL_CODE_REGEX.test(fixed)) { - return false - } - - return fixed -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/sign-up-account-form.tsx b/packages/oauth/oauth-provider/src/assets/app/components/sign-up-account-form.tsx deleted file mode 100644 index ef97af9fb..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/sign-up-account-form.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import { - FormHTMLAttributes, - ReactNode, - SyntheticEvent, - useCallback, - useState, -} from 'react' - -import { Button } from './button' -import { FormCard, FormCardProps } from './form-card' -import { Override } from '../lib/util' -import { Fieldset } from './fieldset' - -export type SignUpAccountFormOutput = { - username: string - password: string -} - -export type SignUpAccountFormProps = Override< - FormCardProps, - { - onSubmit: (credentials: SignUpAccountFormOutput) => void | PromiseLike - submitLabel?: ReactNode - submitAria?: string - - onCancel?: () => void - cancelLabel?: ReactNode - cancelAria?: string - - username?: string - usernamePlaceholder?: string - usernameLabel?: string - usernameAria?: string - usernamePattern?: string - usernameTitle?: string - - passwordPlaceholder?: string - passwordLabel?: string - passwordAria?: string - passwordPattern?: string - passwordTitle?: string - } -> - -export function SignUpAccountForm({ - onSubmit, - submitAria = 'Next', - submitLabel = submitAria, - - onCancel = undefined, - cancelAria = 'Cancel', - cancelLabel = cancelAria, - - username: defaultUsername = '', - usernameLabel = 'Username', - usernameAria = usernameLabel, - usernamePlaceholder = usernameLabel, - usernamePattern, - usernameTitle, - - passwordLabel = 'Password', - passwordAria = passwordLabel, - passwordPlaceholder = passwordLabel, - passwordPattern, - passwordTitle, - - children, - ...props -}: SignUpAccountFormProps & - Omit, keyof SignUpAccountFormProps>) { - const [loading, setLoading] = useState(false) - const [errorMessage, setErrorMessage] = useState(null) - - const doSubmit = useCallback( - async ( - event: SyntheticEvent< - HTMLFormElement & { - username: HTMLInputElement - password: HTMLInputElement - }, - SubmitEvent - >, - ) => { - event.preventDefault() - - const credentials = { - username: event.currentTarget.username.value, - password: event.currentTarget.password.value, - } - - setLoading(true) - setErrorMessage(null) - try { - await onSubmit(credentials) - } catch (err) { - setErrorMessage(parseErrorMessage(err)) - } finally { - setLoading(false) - } - }, - [onSubmit, setErrorMessage, setLoading], - ) - - return ( - - {cancelLabel} - - ) - } - actions={ - - } - {...props} - > -
- - - -
-
- ) -} - -function parseErrorMessage(err: unknown): string { - switch ((err as any)?.message) { - case 'Invalid credentials': - return 'Invalid username or password' - default: - console.error(err) - return 'An unknown error occurred' - } -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/sign-up-disclaimer.tsx b/packages/oauth/oauth-provider/src/assets/app/components/sign-up-disclaimer.tsx deleted file mode 100644 index 053511f7e..000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/sign-up-disclaimer.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { HTMLAttributes } from 'react' -import { LinkDefinition } from '../backend-data' -import { clsx } from '../lib/clsx' - -export type SignUpDisclaimerProps = { - links?: readonly LinkDefinition[] -} - -export function SignUpDisclaimer({ - links, - - className, - ...attrs -}: SignUpDisclaimerProps & - Omit< - HTMLAttributes, - keyof SignUpDisclaimerProps | 'children' - >) { - const relevantLinks = links?.filter( - (l) => l.rel === 'privacy-policy' || l.rel === 'terms-of-service', - ) - - return ( -

- By creating an account you agree to the{' '} - {relevantLinks && relevantLinks.length - ? relevantLinks.map((l, i, a) => ( - - {i > 0 && (i < a.length - 1 ? ', ' : ' and ')} - - {l.title} - - - )) - : 'Terms of Service and Privacy Policy'} - . -

- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/account-identifier.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/account-identifier.tsx new file mode 100644 index 000000000..150a6bb42 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/account-identifier.tsx @@ -0,0 +1,23 @@ +import { JSX } from 'react' +import { Account } from '../../backend-types.ts' +import { Override } from '../../lib/util.ts' + +export type AccountIdentifierProps = Override< + Omit, + { + account: Account + } +> + +export function AccountIdentifier({ + account, + + // b + ...props +}: AccountIdentifierProps) { + return ( + + {account.preferred_username || account.email || account.sub} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/account-image.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/account-image.tsx new file mode 100644 index 000000000..a1c67c62a --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/account-image.tsx @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react' +import { AccountIcon } from './icons.tsx' + +export type AccountIconProps = { + src?: string + alt: string +} + +export function AccountImage({ src, alt }: AccountIconProps) { + const [errored, setErrored] = useState(false) + + useEffect(() => { + setErrored(false) + }, [src]) + + return src && !errored ? ( + {alt} setErrored(true)} + /> + ) : ( +
+ +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/admonition.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/admonition.tsx new file mode 100644 index 000000000..c9e263b93 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/admonition.tsx @@ -0,0 +1,52 @@ +import { JSX, memo } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' +import { AlertIcon, EyeIcon } from './icons.tsx' + +export type AdmonitionProps = Override< + JSX.IntrinsicElements['div'], + { + role: 'alert' | 'status' | 'info' + } +> + +export const Admonition = memo(function Admonition({ + role = 'alert', + children, + className, + ...props +}: AdmonitionProps) { + return ( +
+ {role === 'info' ? ( + + ) : ( + + )} + +
{children}
+
+ ) +}) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/client-name.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/client-name.tsx new file mode 100644 index 000000000..130742520 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/client-name.tsx @@ -0,0 +1,45 @@ +import { Trans } from '@lingui/react/macro' +import { JSX } from 'react' +import type { OAuthClientMetadata } from '@atproto/oauth-types' +import { Override } from '../../lib/util.ts' +import { UrlViewer } from './url-viewer.tsx' + +export type ClientNameProps = Override< + Omit, + { + clientId: string + clientMetadata: OAuthClientMetadata + clientTrusted: boolean + } +> + +export function ClientName({ + clientId, + clientMetadata, + clientTrusted, + + // span + ...attrs +}: ClientNameProps) { + if (clientTrusted && clientMetadata.client_name) { + return {clientMetadata.client_name} + } + + // @NOTE: not using isOAuthClientIdLoopback & isOAuthClientIdDiscoverable from + // @atproto/oauth-types here because 1) we don't need to validate here and 2) + // we prefer not to import un-necessary code to improve bundle size. + + if (clientId.startsWith('http://')) { + return ( + + An application on your device + + ) + } + + if (clientId.startsWith('https://')) { + return + } + + return {clientId} +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/error-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/error-card.tsx new file mode 100644 index 000000000..d80f64eba --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/error-card.tsx @@ -0,0 +1,93 @@ +import { Trans } from '@lingui/react/macro' +import { memo, useEffect, useMemo, useState } from 'react' +import { useRandomString } from '../../hooks/use-random-string.ts' +import { Api } from '../../lib/api.ts' +import { JsonErrorResponse } from '../../lib/json-client.ts' +import { Override } from '../../lib/util.ts' +import { Admonition, AdmonitionProps } from './admonition.tsx' +import { ErrorMessage } from './error-message.tsx' + +export type ErrorCardProps = Override< + Omit, + { + error: unknown + } +> +export const ErrorCard = memo(function ErrorCard({ + error, + + // Admonition + children, + onClick, + onKeyDown, + ...props +}: ErrorCardProps) { + const [inputCount, setInputCount] = useState(0) + // Every 5th input will toggle showing the details + const showDetails = ((inputCount / 5) | 0) % 2 === 1 + + const detailsDivId = useRandomString('error-card-') + + const parsedError = useMemo( + () => + error instanceof JsonErrorResponse + ? // Already parsed: + error + : // If "error" is a json object, try parsing it as a JsonErrorResponse: + Api.parseError(error) ?? error, + [error], + ) + + useEffect(() => { + // For debugging purposes + console.warn('Displayed error details:', parsedError) + + // Reset the input count when the error changes + setInputCount(0) + }, [parsedError]) + + return ( + { + onKeyDown?.(event) + if (!event.defaultPrevented) { + setInputCount((c) => c + 1) + } + }} + onClick={(event) => { + onClick?.(event) + if (!event.defaultPrevented) { + setInputCount((c) => c + 1) + } + }} + {...props} + > + + + {children &&
{children}
} + + +
+ ) +}) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/error-message.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/error-message.tsx new file mode 100644 index 000000000..06b070f23 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/error-message.tsx @@ -0,0 +1,62 @@ +import { Trans } from '@lingui/react/macro' +import { ReactNode, memo } from 'react' +import { + EmailTakenError, + HandleUnavailableError, + InvalidCredentialsError, + RequestExpiredError, + SecondAuthenticationFactorRequiredError, + UnknownRequestUriError, +} from '../../lib/api.ts' +import { JsonErrorResponse } from '../../lib/json-client.ts' + +export type ApiErrorMessageProps = { + error: unknown +} + +export const ErrorMessage = memo(function ErrorMessage({ + error, +}: ApiErrorMessageProps): ReactNode { + if (error instanceof InvalidCredentialsError) { + return Wrong identifier or password + } + + if (error instanceof EmailTakenError) { + return This email is already used + } + + if (error instanceof HandleUnavailableError) { + switch (error.reason) { + case 'syntax': + return The handle is invalid + case 'domain': + return The domain name is not allowed + case 'slur': + return The handle contains inappropriate language + case 'taken': + if (error.description === 'Reserved handle') { + return This handle is reserved + } + return The handle is already in use + default: + return That handle cannot be used + } + } + + if (error instanceof SecondAuthenticationFactorRequiredError) { + return A second authentication factor is required + } + + if ( + error instanceof UnknownRequestUriError || + error instanceof RequestExpiredError + ) { + return This sign-in session has expired + } + + if (error instanceof JsonErrorResponse) { + return Unexpected server response + } + + return An unknown error occurred +}) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/help-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/help-card.tsx new file mode 100644 index 000000000..3b4570903 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/help-card.tsx @@ -0,0 +1,46 @@ +import { Trans } from '@lingui/react/macro' +import { JSX } from 'react' +import { LinkDefinition } from '../../backend-types.ts' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' + +export type HelpCardProps = Override< + Omit, + { + links?: readonly LinkDefinition[] + } +> + +export function HelpCard({ + links, + + className, + ...props +}: HelpCardProps) { + const helpLink = links?.find((l) => l.rel === 'help') + + if (!helpLink) return null + + return ( +

+ + Having trouble?{' '} + + Contact support + + +

+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/icons.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/icons.tsx new file mode 100644 index 000000000..a6b637589 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/icons.tsx @@ -0,0 +1,88 @@ +import type { FunctionComponent, JSX } from 'react' +import { Override } from '../../lib/util.ts' + +export type IconProps = Override< + Omit, + { + /** + * The title of the icon, used for accessibility. + */ + title?: string + } +> + +const makeSvgComponent = (path: string, displayName: string) => { + const SvgComponent: FunctionComponent = ({ title, ...props }) => ( + + {title && {title}} + + + ) + SvgComponent.displayName = displayName + return SvgComponent +} + +export const AccountIcon = makeSvgComponent( + 'M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z', + 'AccountIcon', +) + +export const AlertIcon = makeSvgComponent( + 'M11.14 4.494a.995.995 0 0 1 1.72 0l7.001 12.008a.996.996 0 0 1-.86 1.498H4.999a.996.996 0 0 1-.86-1.498L11.14 4.494Zm3.447-1.007c-1.155-1.983-4.019-1.983-5.174 0L2.41 15.494C1.247 17.491 2.686 20 4.998 20h14.004c2.312 0 3.751-2.509 2.587-4.506L14.587 3.487ZM13 9.019a1 1 0 1 0-2 0v2.994a1 1 0 1 0 2 0V9.02Zm-1 4.731a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5Z', + 'AlertIcon', +) + +export const AtSymbolIcon = makeSvgComponent( + 'M12 4a8 8 0 1 0 4.21 14.804 1 1 0 0 1 1.054 1.7A9.958 9.958 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 1.104-.27 2.31-.949 3.243-.716.984-1.849 1.6-3.331 1.465a4.207 4.207 0 0 1-2.93-1.585c-.94 1.21-2.388 1.94-3.985 1.715-2.53-.356-4.04-2.91-3.682-5.458.358-2.547 2.514-4.586 5.044-4.23.905.127 1.68.536 2.286 1.126a1 1 0 0 1 1.964.368l-.515 3.545v.002a2.222 2.222 0 0 0 1.999 2.526c.75.068 1.212-.21 1.533-.65.358-.493.566-1.245.566-2.067a8 8 0 0 0-8-8Zm-.112 5.13c-1.195-.168-2.544.819-2.784 2.529-.24 1.71.784 3.03 1.98 3.198 1.195.168 2.543-.819 2.784-2.529.24-1.71-.784-3.03-1.98-3.198Z', + 'AtSymbolIcon', +) + +export const CaretRightIcon = makeSvgComponent( + 'M8.293 3.293a1 1 0 0 1 1.414 0l8 8a1 1 0 0 1 0 1.414l-8 8a1 1 0 0 1-1.414-1.414L15.586 12 8.293 4.707a1 1 0 0 1 0-1.414Z', + 'CaretRightIcon', +) + +export const CheckMarkIcon = makeSvgComponent( + 'M21.59 3.193a1 1 0 0 1 .217 1.397l-11.706 16a1 1 0 0 1-1.429.193l-6.294-5a1 1 0 1 1 1.244-1.566l5.48 4.353 11.09-15.16a1 1 0 0 1 1.398-.217Z', + 'CheckMarkIcon', +) + +export const EmailIcon = makeSvgComponent( + 'M4.568 4h14.864c.252 0 .498 0 .706.017.229.019.499.063.77.201a2 2 0 0 1 .874.874c.138.271.182.541.201.77.017.208.017.454.017.706v10.864c0 .252 0 .498-.017.706a2.022 2.022 0 0 1-.201.77 2 2 0 0 1-.874.874 2.022 2.022 0 0 1-.77.201c-.208.017-.454.017-.706.017H4.568c-.252 0-.498 0-.706-.017a2.022 2.022 0 0 1-.77-.201 2 2 0 0 1-.874-.874 2.022 2.022 0 0 1-.201-.77C2 17.93 2 17.684 2 17.432V6.568c0-.252 0-.498.017-.706.019-.229.063-.499.201-.77a2 2 0 0 1 .874-.874c.271-.138.541-.182.77-.201C4.07 4 4.316 4 4.568 4Zm.456 2L12 11.708 18.976 6H5.024ZM20 7.747l-6.733 5.509a2 2 0 0 1-2.534 0L4 7.746V17.4a8.187 8.187 0 0 0 .011.589h.014c.116.01.278.011.575.011h14.8a8.207 8.207 0 0 0 .589-.012v-.013c.01-.116.011-.279.011-.575V7.747Z', + 'EmailIcon', +) + +export const EyeIcon = makeSvgComponent( + 'M12 6.5c3.79 0 7.17 2.13 8.82 5.5-1.65 3.37-5.02 5.5-8.82 5.5S4.83 15.37 3.18 12C4.83 8.63 8.21 6.5 12 6.5m0-2C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5m0 5c1.38 0 2.5 1.12 2.5 2.5s-1.12 2.5-2.5 2.5-2.5-1.12-2.5-2.5 1.12-2.5 2.5-2.5m0-2c-2.48 0-4.5 2.02-4.5 4.5s2.02 4.5 4.5 4.5 4.5-2.02 4.5-4.5-2.02-4.5-4.5-4.5', + 'EyeIcon', +) + +export const EyeSlashIcon = makeSvgComponent( + 'M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2.41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6m-1.07 1.14L13 9.21c.57.25 1.03.71 1.28 1.28l2.07 2.07c.08-.34.14-.7.14-1.07C16.5 9.01 14.48 7 12 7c-.37 0-.72.05-1.07.14M2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45zm7.5 7.5 2.61 2.61c-.04.01-.08.02-.12.02-1.38 0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13m-3.4-3.4 1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 4.5 4.5 4.5.63 0 1.23-.13 1.77-.36l.98.98c-.88.24-1.8.38-2.75.38-3.79 0-7.17-2.13-8.82-5.5.7-1.43 1.72-2.61 2.93-3.53', + 'EyeSlashIcon', +) + +export const LockIcon = makeSvgComponent( + 'M7 7a5 5 0 0 1 10 0v2h1a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h1V7Zm-1 4v9h12v-9H6Zm9-2H9V7a3 3 0 1 1 6 0v2Zm-3 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0v-3a1 1 0 0 1 1-1Z', + 'LockIcon', +) + +export const TokenIcon = makeSvgComponent( + 'M4 5.5a.5.5 0 0 0-.5.5v2.535a.5.5 0 0 0 .25.433A3.498 3.498 0 0 1 5.5 12a3.498 3.498 0 0 1-1.75 3.032.5.5 0 0 0-.25.433V18a.5.5 0 0 0 .5.5h16a.5.5 0 0 0 .5-.5v-2.535a.5.5 0 0 0-.25-.433A3.498 3.498 0 0 1 18.5 12a3.5 3.5 0 0 1 1.75-3.032.5.5 0 0 0 .25-.433V6a.5.5 0 0 0-.5-.5H4ZM2.5 6A1.5 1.5 0 0 1 4 4.5h16A1.5 1.5 0 0 1 21.5 6v3.17a.5.5 0 0 1-.333.472 2.501 2.501 0 0 0 0 4.716.5.5 0 0 1 .333.471V18a1.5 1.5 0 0 1-1.5 1.5H4A1.5 1.5 0 0 1 2.5 18v-3.17a.5.5 0 0 1 .333-.472 2.501 2.501 0 0 0 0-4.716.5.5 0 0 1-.333-.471V6Zm12 2a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Z', + 'TokenIcon', +) + +export const XMarkIcon = makeSvgComponent( + 'M4.293 4.293a1 1 0 0 1 1.414 0L12 10.586l6.293-6.293a1 1 0 1 1 1.414 1.414L13.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414L12 13.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L10.586 12 4.293 5.707a1 1 0 0 1 0-1.414Z', + 'XMarkIcon', +) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/link-anchor.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/link-anchor.tsx new file mode 100644 index 000000000..cc24ead7b --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/link-anchor.tsx @@ -0,0 +1,28 @@ +import { JSX } from 'react' +import { LinkDefinition } from '../../backend-types.ts' +import { Override } from '../../lib/util.ts' +import { LinkTitle } from './link-title.tsx' + +export type LinkAnchorProps = Override< + JSX.IntrinsicElements['a'], + { + link: LinkDefinition + } +> +export function LinkAnchor({ + link, + + // a + children = , + role = 'link', + target = '_blank', + href = link.href, + rel = link.rel, + ...props +}: LinkAnchorProps) { + return ( + + {children} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/link-title.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/link-title.tsx new file mode 100644 index 000000000..048788f43 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/link-title.tsx @@ -0,0 +1,26 @@ +import { Trans } from '@lingui/react/macro' +import { LinkDefinition } from '../../backend-types.ts' +import { MultiLangString } from './multi-lang-string.tsx' + +export type LinkNameProps = { + link: LinkDefinition +} + +export function LinkTitle({ link }: LinkNameProps) { + return ( + Home + ) : link.rel === 'privacy-policy' ? ( + Privacy Policy + ) : link.rel === 'terms-of-service' ? ( + Terms of Service + ) : link.rel === 'help' ? ( + Support + ) : undefined + } + /> + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/multi-lang-string.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/multi-lang-string.tsx new file mode 100644 index 000000000..d971c236c --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/multi-lang-string.tsx @@ -0,0 +1,56 @@ +import { useLingui } from '@lingui/react/macro' +import { ReactNode } from 'react' +import type { LocalizedString } from '../../backend-types.ts' + +export type MultiLangStringProps = { + value: LocalizedString + fallback?: ReactNode +} + +export function MultiLangString({ + value, + fallback, +}: MultiLangStringProps): ReactNode { + const { i18n } = useLingui() + return ( + findMatchingString(value, i18n.locale) ?? + fallback ?? + (typeof value === 'string' ? value : value.en) + ) +} + +/** + * Only returns a string if it matches the desired locale. + */ +function findMatchingString( + value: LocalizedString, + locale: string, +): string | undefined { + switch (typeof value) { + case 'string': + // By convention, string values are in english ("en") + if (locale.startsWith('en')) return value + break + + case 'object': { + // Exact match + const localeMatch = value[locale] + if (typeof localeMatch === 'string') return localeMatch + + // Fallback to language match + const lang = locale.split('-')[0] + const langMatch = value[lang] + if (typeof langMatch === 'string') return langMatch + + // Fallback to any locale from same language (e.g. "pt-PT" -> "pt-BR") + for (const k in value) { + if (k.startsWith(`${lang}-`)) { + const fallbackMatch = value[k] + if (typeof fallbackMatch === 'string') return fallbackMatch + } + } + } + } + + return undefined +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-label.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-label.tsx new file mode 100644 index 000000000..7c1bfbd53 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-label.tsx @@ -0,0 +1,37 @@ +import { Trans, useLingui } from '@lingui/react/macro' +import { JSX } from 'react' +import { PasswordStrength, getPasswordStrength } from '../../lib/password.ts' +import { Override } from '../../lib/util.ts' + +export type PasswordStrengthLabelProps = Override< + Omit, + { + password: string + } +> + +export function PasswordStrengthLabel({ + password, + + // span + ...props +}: PasswordStrengthLabelProps) { + const { t } = useLingui() + const strength = getPasswordStrength(password) + + return ( + + {strength === PasswordStrength.extra ? ( + Extra + ) : strength === PasswordStrength.strong ? ( + Strong + ) : strength === PasswordStrength.moderate ? ( + Moderate + ) : password ? ( + Weak + ) : ( + Missing + )} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-meter.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-meter.tsx new file mode 100644 index 000000000..233c81104 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-meter.tsx @@ -0,0 +1,58 @@ +import { useLingui } from '@lingui/react/macro' +import { JSX } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { PasswordStrength, getPasswordStrength } from '../../lib/password.ts' +import { Override } from '../../lib/util.ts' + +export type PasswordStrengthMeterProps = Override< + Omit< + JSX.IntrinsicElements['div'], + | 'children' + | 'role' + | 'aria-label' + | 'aria-valuemin' + | 'aria-valuemax' + | 'aria-valuenow' + >, + { + password: string + } +> + +export function PasswordStrengthMeter({ + password, + + // div + className, + ...props +}: PasswordStrengthMeterProps) { + const { t } = useLingui() + const strength = password ? getPasswordStrength(password) : 0 + + const colorBg = 'bg-gray-300 dark:bg-slate-500' + const color = + strength === PasswordStrength.extra || strength === PasswordStrength.strong + ? 'bg-success' + : strength === PasswordStrength.moderate + ? 'bg-warning' + : 'bg-error' + + return ( +
+ {Array.from({ length: 4 }, (_, i) => ( +
i ? color : colorBg}`} + /> + ))} +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/url-viewer.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/url-viewer.tsx similarity index 83% rename from packages/oauth/oauth-provider/src/assets/app/components/url-viewer.tsx rename to packages/oauth/oauth-provider/src/assets/app/components/utils/url-viewer.tsx index 4879a5d8e..bdbd4cb19 100644 --- a/packages/oauth/oauth-provider/src/assets/app/components/url-viewer.tsx +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/url-viewer.tsx @@ -1,4 +1,5 @@ -import { HTMLAttributes, useMemo } from 'react' +import { JSX, useMemo } from 'react' +import { Override } from '../../lib/util.ts' export type UrlPartRenderingOptions = { faded?: boolean @@ -12,10 +13,10 @@ export type UrlRendererProps = { path?: boolean | UrlPartRenderingOptions query?: boolean | UrlPartRenderingOptions hash?: boolean | UrlPartRenderingOptions - as?: keyof JSX.IntrinsicElements + as?: string } -export function UrlViewer({ +export function UrlViewer({ url, proto = false, host = true, @@ -23,12 +24,14 @@ export function UrlViewer({ query = false, hash = false, as: As = 'span', - ...attrs -}: UrlRendererProps & HTMLAttributes) { + + // Element + ...props +}: Override) { const urlObj = useMemo(() => new URL(url), [url]) return ( - + {proto && ( any>(fn: F, deps: unknown[]) { + const { showBoundary } = useErrorBoundary() - remember?: boolean + return useCallback( + async (...args: Parameters): Promise>> => { + try { + return await fn(...args) + } catch (error) { + if (error instanceof UnknownRequestUriError) showBoundary(error) + throw error + } + }, + deps.concat(showBoundary), + ) } -export type SignUpData = { - username: string - password: string - extra?: Record +export type UseApiOptions = { + requestUri: string + sessions?: readonly Session[] + newSessionsRequireConsent?: boolean + onRedirected?: () => void } -export function useApi( - { - clientId, - requestUri, - csrfCookie, - sessions: initialSessions, - newSessionsRequireConsent, - }: AuthorizeData, - { - onRedirected, - }: { - onRedirected?: () => void - } = {}, -) { - const csrfToken = useCsrfToken(csrfCookie) ?? '' // Invalid value - const [sessions, setSessions] = useState(initialSessions) +export function useApi({ + requestUri, + sessions: sessionsInit = [], + newSessionsRequireConsent = true, + onRedirected, +}: UseApiOptions) { + const csrfToken = useCsrfToken(`csrf-${requestUri}`) + if (!csrfToken) throw new Error('CSRF token is missing') - const setSession = useCallback( + const api = useMemo(() => new Api(csrfToken), [csrfToken]) + const [sessions, setSessions] = useState(sessionsInit) + + const { i18n } = useLingui() + const { locale } = i18n + + const selectSub = useCallback( (sub: string | null) => { setSessions((sessions) => sub === (sessions.find((s) => s.selected)?.account.sub || null) @@ -46,9 +73,23 @@ export function useApi( [setSessions], ) - const api = useMemo( - () => new Api(requestUri, clientId, csrfToken, newSessionsRequireConsent), - [requestUri, clientId, csrfToken, newSessionsRequireConsent], + const upsertSession = useCallback( + ({ account, consentRequired }: SessionResponse) => { + const session: Session = { + account, + selected: true, + loginRequired: false, + consentRequired: newSessionsRequireConsent || consentRequired, + } + + setSessions((sessions) => + upsert(sessions, session, (s) => s.account.sub === account.sub).map( + // Make sure to de-select any other selected session + (s) => (s === session || !s.selected ? s : { ...s, selected: false }), + ), + ) + }, + [setSessions, newSessionsRequireConsent], ) const performRedirect = useCallback( @@ -59,45 +100,77 @@ export function useApi( [onRedirected], ) - const doSignIn = useCallback( - async (credentials: SignInCredentials): Promise => { - const session = await api.signIn(credentials) - const { sub } = session.account - - setSessions((sessions) => { - return upsert(sessions, session, (s) => s.account.sub === sub).map( - // Make sure to de-select any other selected session - (s) => (s === session || !s.selected ? s : { ...s, selected: false }), - ) - }) + const doSignIn = useSafeCallback( + async (data: Omit, signal?: AbortSignal) => { + const response = await api.fetch( + '/sign-in', + { ...data, locale }, + { signal }, + ) + upsertSession(response) }, - [api, performRedirect, clientId, setSessions], + [api, locale, upsertSession], ) - const doSignUp = useCallback( - (_data: SignUpData) => { - // - throw new Error('Not implemented') + const doInitiatePasswordReset = useSafeCallback( + async ( + data: Omit, + signal?: AbortSignal, + ) => { + await api.fetch( + '/reset-password-request', + { ...data, locale }, + { signal }, + ) + }, + [api, locale], + ) + + const doConfirmResetPassword = useSafeCallback( + async (data: ConfirmResetPasswordData, signal?: AbortSignal) => { + await api.fetch('/reset-password-confirm', data, { signal }) }, [api], ) - const doAccept = useCallback( - async (account: Account) => { - performRedirect(await api.accept(account)) + const doValidateNewHandle = useSafeCallback( + async (data: VerifyHandleAvailabilityData, signal?: AbortSignal) => { + await api.fetch('/verify-handle-availability', data, { signal }) + }, + [api], + ) + + const doSignUp = useSafeCallback( + async (data: Omit, signal?: AbortSignal) => { + const response = await api.fetch( + '/sign-up', + { ...data, locale }, + { signal }, + ) + upsertSession(response) + }, + [api, locale, upsertSession], + ) + + const doAccept = useSafeCallback( + async (data: AcceptData) => { + performRedirect(api.buildAcceptUrl(data)) }, [api, performRedirect], ) - const doReject = useCallback(async () => { - performRedirect(await api.reject()) + const doReject = useSafeCallback(async () => { + performRedirect(api.buildRejectUrl()) }, [api, performRedirect]) return { sessions, - setSession, + selectSub, doSignIn, + doInitiatePasswordReset, + doConfirmResetPassword, + doValidateNewHandle, doSignUp, doAccept, doReject, diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-async-action.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-async-action.ts new file mode 100644 index 000000000..d04fec81d --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-async-action.ts @@ -0,0 +1,120 @@ +import { + ForwardedRef, + useCallback, + useEffect, + useImperativeHandle, + useRef, + useState, +} from 'react' + +export type AsyncActionController = { + reset: () => void +} + +export type UseAsyncActionOptions = { + ref?: ForwardedRef + onLoading?: (loading: boolean) => void + onError?: (error: Error | undefined) => void +} + +export function useAsyncAction( + fn: (signal: AbortSignal) => void | PromiseLike, + { ref, onLoading, onError }: UseAsyncActionOptions = {}, +) { + const [loading, setLoading] = useState(false) + const [error, setError] = useState() + + const doSetError = useCallback( + (error: Error | undefined) => { + setError(error) + onError?.(error) + }, + [onError], + ) + + const doSetLoading = useCallback( + (loading: boolean) => { + setLoading(loading) + onLoading?.(loading) + }, + [onLoading], + ) + + const controllerRef = useRef(null) + + const resetRef = useRef<() => void>(null) + useEffect(() => { + resetRef.current = () => { + controllerRef.current?.abort() + controllerRef.current = null + doSetError(undefined) + doSetLoading(false) + } + return () => { + resetRef.current = null + } + }, [doSetError, doSetLoading]) + + useImperativeHandle( + ref, + (): AsyncActionController => ({ + reset: () => resetRef.current?.(), + }), + [], + ) + + // Cancel pending action when unmounted + useEffect(() => { + return () => { + controllerRef.current?.abort() + controllerRef.current = null + } + }, []) + + const run = useCallback(async (): Promise => { + // Cancel previous run + controllerRef.current?.abort() + + doSetLoading(true) + doSetError(undefined) + + const controller = new AbortController() + const { signal } = controller + + controllerRef.current = controller + + try { + await fn(signal) + } catch (err) { + if (controller === controllerRef.current) { + doSetError(err instanceof Error ? err : new Error(String(err))) + } else { + if (!isAbortReason(signal, err)) { + console.warn('Async action error after abort', err) + } + } + } finally { + if (controller === controllerRef.current) { + controllerRef.current = null + doSetLoading(false) + } + + controller.abort() + } + }, [fn, doSetLoading, doSetError]) + + return { + loading, + error, + run, + } +} + +function isAbortReason(signal: AbortSignal, err: unknown): boolean { + return ( + signal.aborted && + (signal.reason === err || + signal.reason === err?.['cause'] || + (err instanceof DOMException && err.name === 'AbortError')) + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-browser-color-scheme.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-browser-color-scheme.ts new file mode 100644 index 000000000..478463614 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-browser-color-scheme.ts @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react' + +const query = + typeof window === 'undefined' + ? null + : window.matchMedia('(prefers-color-scheme: dark)') + +export function useBrowserColorScheme() { + const [theme, setTheme] = useState<'light' | 'dark'>( + query?.matches ? 'dark' : 'light', + ) + + useEffect(() => { + if (!query) return + + const listener = () => { + setTheme(query.matches ? 'dark' : 'light') + } + + query.addEventListener('change', listener) + + return () => { + query.removeEventListener('change', listener) + } + + // @NOTE "query" is a global constant and does not need to be part of the + // array bellow: + }, []) + + return theme +} diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-csrf-token.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-csrf-token.ts index ee272abcb..36f5cd785 100644 --- a/packages/oauth/oauth-provider/src/assets/app/hooks/use-csrf-token.ts +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-csrf-token.ts @@ -1,4 +1,4 @@ -import { cookies } from '../cookies' +import { cookies } from '../cookies.ts' export function useCsrfToken(cookieName: string) { return cookies[cookieName] diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-random-string.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-random-string.ts new file mode 100644 index 000000000..6a69bdce7 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-random-string.ts @@ -0,0 +1,37 @@ +import { useEffect, useState } from 'react' + +export const UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +export const LOWER = UPPER.toLowerCase() as Lowercase +export const DIGITS = '0123456789' + +export const ALPHANUMERIC = `${UPPER}${LOWER}${DIGITS}` as const + +export type UseRandomStringOptions = BuildRandomStringOptions & { + prefix?: string + suffix?: string +} + +export function useRandomString(options?: UseRandomStringOptions) { + const [state, setState] = useState(() => buildRandomString(options)) + useEffect(() => { + setState(buildRandomString(options)) + }, [options?.length, options?.alphabet]) + + return `${options?.prefix ?? ''}${state}${options?.suffix ?? ''}` +} + +type BuildRandomStringOptions = { + length?: number + alphabet?: string +} + +function buildRandomString({ + length = 16, + alphabet = ALPHANUMERIC, +}: BuildRandomStringOptions = {}) { + return Array.from({ length }, () => getRandomCharFrom(alphabet)).join('') +} + +function getRandomCharFrom(alphabet: string) { + return alphabet.charAt((Math.random() * alphabet.length) | 0) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-stepper.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-stepper.ts new file mode 100644 index 000000000..a4d6167a2 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-stepper.ts @@ -0,0 +1,87 @@ +import { useCallback, useEffect, useState } from 'react' + +export type DisabledStep = false | null | undefined +export type Step = { + invalid: boolean +} + +const isEnabled = ( + s: S, +): s is S extends DisabledStep ? never : S => s != null && s !== false +const isRequired = ( + s: S, +): s is S extends DisabledStep ? never : S & { invalid: true } => + isEnabled(s) && s.invalid === true +const isCompleted = ( + s: S, +): s is S extends DisabledStep ? S : S & { invalid: false } => + !isEnabled(s) || s.invalid === false + +export function useStepper( + steps: readonly (S | DisabledStep)[], +) { + const firstIdx = steps.findIndex(isEnabled) + const lastIdx = steps.findLastIndex(isEnabled) + const requiredIdx = steps.findIndex(isRequired) + + const [currentIdx, setCurrentIdx] = useState(firstIdx) + + const to = useCallback( + (idx: number) => { + if (idx !== -1 && steps[idx]) { + setCurrentIdx(idx) + return true + } else { + return false + } + }, + [steps.map(isEnabled).join()], + ) + + const prevIdx = steps.findLastIndex((s, i) => isEnabled(s) && i < currentIdx) + const nextIdx = steps.findIndex((s, i) => isEnabled(s) && i > currentIdx) + + const toFirst = useCallback(() => to(firstIdx), [to, firstIdx]) + const toLast = useCallback(() => to(lastIdx), [to, lastIdx]) + const toPrev = useCallback(() => to(prevIdx), [to, prevIdx]) + const toNext = useCallback(() => to(nextIdx), [to, nextIdx]) + const toRequired = useCallback(() => to(requiredIdx), [to, requiredIdx]) + + // Step number in user friendly terms (accounting for disabled steps) + const currentPosition = + currentIdx + + // use "1 indexed position" (for user friendliness): + 1 + + // Adjust the position by counting the number of disabled steps before the + // current step (if any): + steps.reduce( + (acc, s, i) => (i >= currentIdx || isEnabled(s) ? acc : acc - 1), + 0, + ) + + const count = steps.filter(isEnabled).length + const completed = steps.every(isCompleted) + + const current = + currentIdx === -1 || !steps[currentIdx] ? undefined : steps[currentIdx] + + // Fool-proof (reset current step in case the current step becomes disabled) + const broken = currentIdx === -1 + useEffect(() => { + if (broken) toFirst() + }, [broken]) + + return { + current, + currentPosition, + count, + completed, + atFirst: currentPosition === 1, + atLast: currentPosition === count, + toFirst, + toLast, + toPrev, + toNext, + toRequired, + } +} diff --git a/packages/oauth/oauth-provider/src/assets/app/index.html b/packages/oauth/oauth-provider/src/assets/app/index.html new file mode 100644 index 000000000..815d51605 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/index.html @@ -0,0 +1,182 @@ + + + + + + + OAuth Provider + + +
+ + + + + + diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/api.ts b/packages/oauth/oauth-provider/src/assets/app/lib/api.ts index 1237e9a22..34d0496e8 100644 --- a/packages/oauth/oauth-provider/src/assets/app/lib/api.ts +++ b/packages/oauth/oauth-provider/src/assets/app/lib/api.ts @@ -1,98 +1,267 @@ -import { FetchResponseError, Json } from '@atproto-labs/fetch' -import { Account, Session } from '../backend-data' +import { Account } from '../backend-types.ts' +import { + JsonClient, + JsonErrorPayload, + JsonErrorResponse, +} from './json-client.ts' -export class Api { - constructor( - private requestUri: string, - private clientId: string, - private csrfToken: string, - private newSessionsRequireConsent: boolean, - ) {} +export type { Options } from './json-client.ts' - async signIn(credentials: { - username: string - password: string - remember?: boolean - }): Promise { - const response = await fetch('/oauth/authorize/sign-in', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - mode: 'same-origin', - body: JSON.stringify({ - csrf_token: this.csrfToken, - request_uri: this.requestUri, - client_id: this.clientId, - credentials, - }), - }) +export type AcceptData = { + sub: string +} - const json: Json = await response.json() +export type SignInData = { + locale: string + username: string + password: string + emailOtp?: string + remember?: boolean +} - if (response.ok) { - const data = json as { - account: Account - consentRequired: boolean - } +export type SignUpData = { + locale: string + handle: string + email: string + password: string + inviteCode?: string + hcaptchaToken?: string +} - return { - account: data.account, +export type InitiatePasswordResetData = { + locale: string + email: string +} - selected: true, - loginRequired: false, - consentRequired: this.newSessionsRequireConsent || data.consentRequired, - } - } else if ( - response.status === 400 && - json?.['error'] === 'invalid_request' && - json?.['error_description'] === 'Invalid credentials' - ) { - throw new InvalidCredentialsError() - } else if ( - response.status === 401 && - json?.['error'] === 'second_authentication_factor_required' - ) { - const data = json as { - type: 'emailOtp' - hint: string - } +export type ConfirmResetPasswordData = { + token: string + password: string +} - throw new SecondAuthenticationFactorRequiredError(data.type, data.hint) - } else { - throw new FetchResponseError(response) +export type VerifyHandleAvailabilityData = { + handle: string +} + +export type SessionResponse = { + account: Account + consentRequired: boolean +} + +export class Api extends JsonClient<{ + '/verify-handle-availability': { + input: VerifyHandleAvailabilityData + output: void + } + '/sign-up': { + input: SignUpData + output: SessionResponse + } + '/sign-in': { + input: SignInData + output: SessionResponse + } + '/reset-password-request': { + input: InitiatePasswordResetData + output: void + } + '/reset-password-confirm': { + input: ConfirmResetPasswordData + output: void + } +}> { + constructor(csrfToken: string) { + const baseUrl = new URL('/oauth/authorize', window.origin).toString() + super(baseUrl, csrfToken) + } + + public buildAcceptUrl(data: AcceptData): URL { + const url = new URL(`${this.baseUrl}/accept`) + url.searchParams.set('account_sub', data.sub) + url.searchParams.set('csrf_token', this.csrfToken) + return url + } + + public buildRejectUrl(): URL { + const url = new URL(`${this.baseUrl}/reject`) + url.searchParams.set('csrf_token', this.csrfToken) + return url + } + + public static override parseError( + json: unknown, + ): undefined | JsonErrorResponse { + // @NOTE Most specific errors first ! + if (SecondAuthenticationFactorRequiredError.is(json)) { + return new SecondAuthenticationFactorRequiredError(json) } - } - - async accept(account: Account): Promise { - const url = new URL('/oauth/authorize/accept', window.origin) - url.searchParams.set('request_uri', this.requestUri) - url.searchParams.set('account_sub', account.sub) - url.searchParams.set('client_id', this.clientId) - url.searchParams.set('csrf_token', this.csrfToken) - - return url - } - - async reject(): Promise { - const url = new URL('/oauth/authorize/reject', window.origin) - url.searchParams.set('request_uri', this.requestUri) - url.searchParams.set('client_id', this.clientId) - url.searchParams.set('csrf_token', this.csrfToken) - - return url + if (InvalidCredentialsError.is(json)) { + return new InvalidCredentialsError(json) + } + if (HandleUnavailableError.is(json)) { + return new HandleUnavailableError(json) + } + if (EmailTakenError.is(json)) { + return new EmailTakenError(json) + } + if (RequestExpiredError.is(json)) { + return new RequestExpiredError(json) + } + if (UnknownRequestUriError.is(json)) { + return new UnknownRequestUriError(json) + } + if (InvalidRequestError.is(json)) { + return new InvalidRequestError(json) + } + if (AccessDeniedError.is(json)) { + return new AccessDeniedError(json) + } + return super.parseError(json) } } -export class InvalidCredentialsError extends Error { - constructor() { - super('Invalid credentials') - } -} - -export class SecondAuthenticationFactorRequiredError extends Error { +export type AccessDeniedPayload = JsonErrorPayload<'access_denied'> +export class AccessDeniedError< + P extends AccessDeniedPayload = AccessDeniedPayload, +> extends JsonErrorResponse

{ constructor( - public type: 'emailOtp', - public hint: string, + payload: P, + message = payload.error_description || 'Access denied', ) { - super(`${type} authentication factor required (hint: ${hint})`) + super(payload, message) + } + + static is(json: unknown): json is AccessDeniedPayload { + return super.is(json) && json.error === 'access_denied' + } +} + +export type InvalidRequestPayload = JsonErrorPayload<'invalid_request'> +export class InvalidRequestError< + P extends InvalidRequestPayload = InvalidRequestPayload, +> extends JsonErrorResponse

{ + constructor( + payload: P, + message = payload.error_description || 'Invalid request', + ) { + super(payload, message) + } + + static is(json: unknown): json is InvalidRequestPayload { + return super.is(json) && json.error === 'invalid_request' + } +} + +export type RequestExpiredPayload = AccessDeniedPayload & { + error_description: 'This request has expired' +} +export class RequestExpiredError< + P extends RequestExpiredPayload = RequestExpiredPayload, +> extends AccessDeniedError

{ + static is(json: unknown): json is RequestExpiredPayload { + return ( + super.is(json) && json.error_description === 'This request has expired' + ) + } +} + +export type InvalidCredentialsPayload = InvalidRequestPayload & { + error_description: 'Invalid identifier or password' +} +export class InvalidCredentialsError< + P extends InvalidCredentialsPayload = InvalidCredentialsPayload, +> extends InvalidRequestError

{ + static is(json: unknown): json is InvalidCredentialsPayload { + return ( + super.is(json) && + json.error_description === 'Invalid identifier or password' + ) + } +} + +export type UnknownRequestPayload = InvalidRequestPayload & { + error_description: 'Unknown request_uri' +} +export class UnknownRequestUriError< + P extends UnknownRequestPayload = UnknownRequestPayload, +> extends InvalidRequestError

{ + static is(json: unknown): json is UnknownRequestPayload { + return super.is(json) && json.error_description === 'Unknown request_uri' + } +} +export type EmailTakenPayload = InvalidRequestPayload & { + error_description: 'Email already taken' +} +export class EmailTakenError< + P extends EmailTakenPayload = EmailTakenPayload, +> extends InvalidRequestError

{ + static is(json: unknown): json is EmailTakenPayload { + return super.is(json) && json.error_description === 'Email already taken' + } +} + +export type HandleUnavailablePayload = + JsonErrorPayload<'handle_unavailable'> & { + reason: 'syntax' | 'domain' | 'slur' | 'taken' + } +export class HandleUnavailableError< + P extends HandleUnavailablePayload = HandleUnavailablePayload, +> extends JsonErrorResponse

{ + constructor( + payload: P, + message = payload.error_description || 'That handle cannot be used', + ) { + super(payload, message) + } + + get reason() { + return this.payload.reason + } + + static is(json: unknown): json is HandleUnavailablePayload { + return ( + super.is(json) && + json.error === 'handle_unavailable' && + 'reason' in json && + (json.reason === 'syntax' || + json.reason === 'domain' || + json.reason === 'slur' || + json.reason === 'taken') + ) + } +} + +export type SecondAuthenticationFactorRequiredPayload = + JsonErrorPayload<'second_authentication_factor_required'> & { + type: 'emailOtp' + hint: string + } +export class SecondAuthenticationFactorRequiredError< + P extends + SecondAuthenticationFactorRequiredPayload = SecondAuthenticationFactorRequiredPayload, +> extends JsonErrorResponse

{ + constructor( + payload: P, + message = payload.error_description || + `${payload.type} authentication factor required (hint: ${payload.hint})`, + ) { + super(payload, message) + } + + get type() { + return this.payload.type + } + get hint() { + return this.payload.hint + } + + static is(json: unknown): json is SecondAuthenticationFactorRequiredPayload { + return ( + super.is(json) && + json.error === 'second_authentication_factor_required' && + 'type' in json && + json.type === 'emailOtp' && + 'hint' in json && + typeof json.hint === 'string' + ) } } diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/clsx.ts b/packages/oauth/oauth-provider/src/assets/app/lib/clsx.ts index 08fbfbfb2..91d453ca2 100644 --- a/packages/oauth/oauth-provider/src/assets/app/lib/clsx.ts +++ b/packages/oauth/oauth-provider/src/assets/app/lib/clsx.ts @@ -1,9 +1,6 @@ -export function clsx( - a?: string, - ...args: readonly (string | undefined)[] -): string | undefined { - if (args.length === 0) return a - const b = clsx(...args) - if (a && b) return `${a} ${b}` - return a || b +type ClsxArg = string | false | undefined + +export function clsx(...args: [ClsxArg, ...ClsxArg[]]): string | undefined { + const filtered = args.filter(Boolean) as string[] + return filtered.length > 0 ? filtered.join(' ') : undefined } diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/json-client.ts b/packages/oauth/oauth-provider/src/assets/app/lib/json-client.ts new file mode 100644 index 000000000..554949b44 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/lib/json-client.ts @@ -0,0 +1,94 @@ +// Using a type import to avoid bundling this lib +import type { Json } from '@atproto-labs/fetch' + +export { type Json } + +export type Options = { + signal?: AbortSignal +} + +export type EndpointDefinition = { + input: Json + output: Json | void +} + +export class JsonClient { + constructor( + protected readonly baseUrl: string, + protected readonly csrfToken: string, + ) {} + + public async fetch

( + path: P, + payload: E[P]['input'], + options?: Options, + ): Promise { + const response = await fetch(`${this.baseUrl}${path}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': this.csrfToken, + }, + mode: 'same-origin', + body: JSON.stringify(payload), + signal: options?.signal, + }) + + if (response.status === 204) { + return undefined + } + + return response.json().then((json: Json) => { + if (response.ok) return json as E[P]['output'] + else throw this.parseError(response, json) + }) + } + + protected parseError(response: Response, json: Json): Error { + const Class = this.constructor as typeof JsonClient + const error = Class.parseError(json) + if (error) return error + + return new Error('Invalid JSON response', { cause: response }) + } + + public static parseError(json: unknown): undefined | JsonErrorResponse { + if (JsonErrorResponse.is(json)) { + return new JsonErrorResponse(json) + } + } +} + +export type JsonErrorPayload = { + error: E + error_description?: string +} + +export class JsonErrorResponse< + P extends JsonErrorPayload = JsonErrorPayload, +> extends Error { + constructor( + public readonly payload: P, + message = payload.error_description, + ) { + super(message || `Error "${payload.error}"`) + } + + get error(): string { + return this.payload.error + } + + get description(): string | undefined { + return this.payload.error_description + } + + static is(json: unknown): json is JsonErrorPayload { + return ( + json != null && + typeof json === 'object' && + typeof json['error'] === 'string' && + (json['error_description'] === undefined || + typeof json['error_description'] === 'string') + ) + } +} diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/password.ts b/packages/oauth/oauth-provider/src/assets/app/lib/password.ts new file mode 100644 index 000000000..29c30e0e3 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/lib/password.ts @@ -0,0 +1,98 @@ +export const MIN_PASSWORD_LENGTH = 8 + +const EMOJI = + /(\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/ +const UPPER = /[A-Z]/ +const LOWER = /[a-z]/ +const DEC = /[0-9]/ +const SPECIAL = /[^a-zA-Z0-9]/ + +export enum PasswordStrength { + weak = 1, + moderate = 2, + strong = 3, + extra = 4, +} + +export function getPasswordStrength(pwd: string): PasswordStrength { + if (pwd.length < MIN_PASSWORD_LENGTH) { + return PasswordStrength.weak + } + + // Very long passwords + if (pwd.length >= MIN_PASSWORD_LENGTH + 12) { + return PasswordStrength.extra + } + + // Long passwords + if (pwd.length >= MIN_PASSWORD_LENGTH + 8) { + if (matches(pwd, [SPECIAL])) { + return PasswordStrength.extra + } + if (matches(pwd, [UPPER, LOWER, DEC], 2)) { + return PasswordStrength.extra + } + return PasswordStrength.strong + } + + // Emojis make passwords strong + if (pwd.length >= MIN_PASSWORD_LENGTH) { + if (matches(pwd, [EMOJI])) { + return PasswordStrength.strong + } + } + + // Pretty long passwords + if (pwd.length >= MIN_PASSWORD_LENGTH + 6) { + if (matches(pwd, [SPECIAL])) { + return PasswordStrength.strong + } + if (matches(pwd, [UPPER, LOWER, DEC], 2)) { + return PasswordStrength.strong + } + // Only 1 type of alpha-num characters + return PasswordStrength.moderate + } + + // Longish password + if (pwd.length >= MIN_PASSWORD_LENGTH + 4) { + if (matches(pwd, [SPECIAL])) { + return PasswordStrength.moderate + } + if (matches(pwd, [UPPER, LOWER, DEC], 2)) { + return PasswordStrength.moderate + } + + // Only 1 type of alpha-num characters + return PasswordStrength.weak + } + + // Short password (8-11 characters) + if (pwd.length >= MIN_PASSWORD_LENGTH) { + if (matches(pwd, [SPECIAL])) { + return PasswordStrength.moderate + } + if (matches(pwd, [UPPER, LOWER, DEC])) { + return PasswordStrength.moderate + } + } + + return PasswordStrength.weak +} + +function matches( + pwd: string, + regexps: RegExp[], + regexpsCountToMatch: number = regexps.length, +): boolean { + if (regexpsCountToMatch < 1 || regexpsCountToMatch > regexps.length) { + throw new TypeError('Invalid regexpsCountToMatch') + } + for (const regexp of regexps) { + if (regexp.test(pwd)) { + regexpsCountToMatch-- + if (regexpsCountToMatch === 0) return true + } + } + return false +} diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/ref.ts b/packages/oauth/oauth-provider/src/assets/app/lib/ref.ts new file mode 100644 index 000000000..f9c966855 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/lib/ref.ts @@ -0,0 +1,17 @@ +import { ForwardedRef } from 'react' + +export function updateRef(ref: ForwardedRef, value: T | null) { + if (typeof ref === 'function') { + ref(value) + } else if (ref) { + ref.current = value + } +} + +export function mergeRefs(refs: readonly (ForwardedRef | undefined)[]) { + return (value: T | null) => { + for (const ref of refs) { + if (ref) updateRef(ref, value) + } + } +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/an/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/an/messages.po new file mode 100644 index 000000000..12d1c9bb2 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/an/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: an\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ast/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ast/messages.po new file mode 100644 index 000000000..050b5bc94 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ast/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: ast\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ca/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ca/messages.po new file mode 100644 index 000000000..243c79422 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ca/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/da/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/da/messages.po new file mode 100644 index 000000000..bf02908f2 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/da/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: da\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/de/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/de/messages.po new file mode 100644 index 000000000..f8644abc6 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/de/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: de\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/el/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/el/messages.po new file mode 100644 index 000000000..6d7e44425 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/el/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: el\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/en-GB/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/en-GB/messages.po new file mode 100644 index 000000000..08c02c01a --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/en-GB/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: en-GB\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/en/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/en/messages.po new file mode 100644 index 000000000..79c9a01ef --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/en/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:03+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: en\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "<0/> is asking for permission to access your account (<1/>)." + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "2FA Confirmation" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "A second authentication factor is required" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "Access your account data (except chat messages)" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "Access your chat messages" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "Account" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "Already have a code?" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "An application on your device" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "An unknown error occurred" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "Another account" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "Authenticate" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "Authorize" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "Back" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "Between {minLength} and {maxLength} characters" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "By creating an account you agree to the {0} and the {1} of this service." + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "Cancel" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "Check your {0} email for a login code and enter it here." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "Choose a username" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "Code" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "Confirm" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "Confirm your password to continue" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "Confirmation code" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "Create a new account" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "Create Account" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "Deny access" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "Description" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "Email" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "Email address" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "Enter a password" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "Enter the code you received to reset your password." + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "Enter your email address" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "Enter your new password" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "Enter your password" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "Enter your username and password" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "Error" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "Extra" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "Forgot Password" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "Forgot?" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "Grant access to your <0>{0} account" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "Having trouble? <0>Contact support" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "Hide" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "Home" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "Identifier" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "Interface language selector" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "Invalid" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "Invite code" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "Let's get your password reset!" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "Login complete" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "Login to account that is not listed" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "Looks like {example}" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "Make visible" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "Missing" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "Moderate" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "Name" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "New password" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "Next" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "Okay" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "Only letters, numbers, and hyphens" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "Password" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "Password strength" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "Password strength indicator" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "Password Updated" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "Password updated!" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "Password with at least {MIN_PASSWORD_LENGTH} characters" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "Privacy Policy" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "Remember this account on this device" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "Requested permissions" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "Reset code" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "Reset Password" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "Reset your password" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "Select domain" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "Select from an existing account" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "Session" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "Sign in" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "Sign in as {0}" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "Sign in as..." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "Sign up" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "Step {currentPosition} of {count}" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "Strong" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "Submit" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "Support" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "Terms of Service" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "That handle cannot be used" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "The domain name is not allowed" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "The handle contains inappropriate language" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "The handle is already in use" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "The handle is invalid" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "This email is already used" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "This handle is reserved" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "This sign-in session has expired" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "Type your desired username" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "Unexpected server response" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "Uniquely identify you" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "Username or email address" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "Valid" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "Valid email address or username" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "Verify you are human" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "Warning" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "We're so excited to have you join us!" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "Weak" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "Wrong identifier or password" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "You are being redirected..." + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "You can change this username to any domain name you control after your account is set up." + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "You can now sign in with your new password." + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "You will receive an email with a \"reset code\". Enter that code here then enter your new password." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "Your account" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "Your full username will be: {0}" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "Your password has been updated!" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/es/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/es/messages.po new file mode 100644 index 000000000..5540cecb0 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/es/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: es\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/eu/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/eu/messages.po new file mode 100644 index 000000000..d92d17717 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/eu/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: eu\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/fi/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/fi/messages.po new file mode 100644 index 000000000..fac181cba --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/fi/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: fi\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/fr/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/fr/messages.po new file mode 100644 index 000000000..8a8434475 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/fr/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:03+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: fr\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "<0/> demande la permission d'accéder à votre compte (<1/>)." + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "Confirmation 2FA" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "Un second facteur d'authentification est requis" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "Accéder aux données de votre compte (sauf les messages de chat)" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "Accéder à vos messages de chat" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "Compte d'utilisateur" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "Vous avez déjà un code ?" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "Une application sur votre appareil" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "Une erreur inconnue s'est produite" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "Un autre compte" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "Authentification" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "Authoriser l'accès" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "Retour" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "Entre {minLength} et {maxLength} caractères" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "En cliquant sur <0>Authoriser l'accès, vous autorisez cette application à effectuer les actions suivantes conformément à leurs <1>conditions d'utilisation et leur <2>politique de confidentialité :" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "En créant un compte, vous acceptez les {0} et la {1} de ce service." + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "Annuler" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "Vérifiez vos emails {0} pour un code de connexion et saisissez-le ici." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "Choisissez un nom d'utilisateur" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "Code" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "Confirmer" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "Confirmez votre mot de passe pour continuer" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "Code de confirmation" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "Créer un nouveau compte" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "Créer un compte" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "Refuser l'accès" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "Description" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "Email" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "Adresse email" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "Saisissez un mot de passe" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "Saisissez le code que vous avez reçu pour réinitialiser votre mot de passe." + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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'email que vous avez utilisé pour créer votre compte. Nous vous enverrons un \"code de réinitialisation\" pour que vous puissiez définir un nouveau mot de passe." + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "Saisissez votre adresse email" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "Saisissez votre nouveau mot de passe" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "Saisissez votre mot de passe" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "Saisissez votre nom d'utilisateur et votre mot de passe" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "Erreur" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "Mot de passe oublié" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "Oublié ?" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "Accorder l'accès à votre compte <0>{0}" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "Vous rencontrez des difficultés ? <0>Contactez le support" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "Cacher" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "Accueil" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "Identifiant" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "Sélecteur de langue" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "Invalide" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "Code d'invitation" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "Réinitialisons votre mot de passe !" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "Connexion terminée" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "Se connecter à un compte qui n'est pas listé" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "Ressemble à {example}" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "Rendre visible" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "Manquant" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "Modéré" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "Nom" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "Nouveau mot de passe" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "Suivant" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "OK" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "Uniquement des lettres, des chiffres et des traits d'union" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "Mot de passe" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "Complexité du mot de passe" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "Indicateur de complexité du mot de passe" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "Mot de passe mis à jour" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "Mot de passe mis à jour !" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "Mot de passe d'au moins {MIN_PASSWORD_LENGTH} caractères" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "Veuillez vérifier le nom de domaine du site web avant de saisir votre mot de passe. N'entrez jamais votre mot de passe sur un domaine auquel vous ne faites pas confiance." + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "Politique de Confidentialité" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "Se souvenir de ce compte sur cet appareil" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "Permissions demandées" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "Code de réinitialisation" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "Réinitialiser le mot de passe" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "Réinitialisez votre mot de passe" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "Sélectionner le domaine" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "Sélectionner un compte" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "Session" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "Se connecter" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "Se connecter en tant que {0}" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "Se connecter en tant que..." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "S'inscrire" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "Étape {currentPosition} sur {count}" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "Fort" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "Soumettre" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "Support" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "Conditions d'Utilisation" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "Ce pseudonyme ne peut pas être utilisé" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "Le nom de domaine n'est pas autorisé" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "Le pseudonyme contient un langage inapproprié" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "Ce pseudonyme est déjà utilisé" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "Le pseudonyme est invalide" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "Cet email est déjà utilisé" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "Ce pseudonyme est réservé" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "Cette session de connexion a expiré" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "Saisissez le nom d'utilisateur souhaité" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "Réponse inattendue du serveur" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "Vous identifier de façon unique" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "Nom d'utilisateur ou adresse email" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "Valide" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "Adresse email ou nom d'utilisateur valide" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "Vérifiez que vous êtes humain" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "Avertissement" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "Nous sommes ravis de vous accueillir parmi nous !" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "Faible" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "Identifiant ou mot de passe incorrect" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "Vous êtes redirigé..." + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "Vous pourrez changer ce nom d'utilisateur pour n'importe quel nom de domaine sous votre contrôle une fois votre compte créé." + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "Vous pouvez maintenant vous connecter avec votre nouveau mot de passe." + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "Vous aver reçu un email avec un \"code de réinitialisation\". Saisissez ce code ici puis entrez votre nouveau mot de passe." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "Votre compte" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "Votre nom d'utilisateur complet sera : {0}" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "Votre mot de passe a été mis à jour !" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ga/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ga/messages.po new file mode 100644 index 000000000..83e5f16ea --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ga/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: ga\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/gl/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/gl/messages.po new file mode 100644 index 000000000..08c019f01 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/gl/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: gl\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/hi/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/hi/messages.po new file mode 100644 index 000000000..ca5bdcfa2 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/hi/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: hi\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/hu/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/hu/messages.po new file mode 100644 index 000000000..58d146435 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/hu/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: hu\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ia/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ia/messages.po new file mode 100644 index 000000000..e459921b0 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ia/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: ia\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/id/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/id/messages.po new file mode 100644 index 000000000..f2e96668f --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/id/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: id\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/it/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/it/messages.po new file mode 100644 index 000000000..77db5cc0e --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/it/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: it\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ja/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ja/messages.po new file mode 100644 index 000000000..b5fcde7e3 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ja/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: ja\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/km/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/km/messages.po new file mode 100644 index 000000000..c82a1a6e9 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/km/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: km\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ko/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ko/messages.po new file mode 100644 index 000000000..8c466da56 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ko/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: ko\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/load.ts b/packages/oauth/oauth-provider/src/assets/app/locales/load.ts new file mode 100644 index 000000000..4026701d4 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/load.ts @@ -0,0 +1,8 @@ +import { Messages } from '@lingui/core' + +// @NOTE run "pnpm run po:compile" to compile the messages from the PO files + +export async function loadMessages(locale: string): Promise { + const { messages } = await import(`./${locale}/messages.ts`) + return messages +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/locale-context.ts b/packages/oauth/oauth-provider/src/assets/app/locales/locale-context.ts new file mode 100644 index 000000000..4b793a4d0 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/locale-context.ts @@ -0,0 +1,19 @@ +import { createContext, useContext } from 'react' +// @NOTE run "pnpm run po:compile" to compile the messages from the PO files +import { KnownLocale } from './locales.ts' + +export type LocaleContextValue = { + locale: string + locales: Partial> + setLocale: (locale: KnownLocale) => void +} + +export const LocaleContext = createContext(null) + +export function useLocaleContext(): LocaleContextValue { + const context = useContext(LocaleContext) + if (!context) { + throw new Error('useLocaleContext must be used within a LocaleProvider') + } + return context +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/locale-provider.tsx b/packages/oauth/oauth-provider/src/assets/app/locales/locale-provider.tsx new file mode 100644 index 000000000..cd6a177ab --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/locale-provider.tsx @@ -0,0 +1,112 @@ +import { I18n } from '@lingui/core' +import { I18nProvider } from '@lingui/react' +import { ReactNode, useEffect, useMemo, useState } from 'react' +// @NOTE run "pnpm run po:compile" to compile the messages from the PO files +import { messages as en } from './en/messages.ts' +import { loadMessages } from './load.ts' +import { LocaleContext, LocaleContextValue } from './locale-context.ts' +import { KnownLocale, knownLocales, locales, resolveLocale } from './locales.ts' + +export type LocaleProviderProps = { + availableLocales?: readonly string[] + children?: ReactNode +} + +export function LocaleProvider({ + availableLocales, + children, +}: LocaleProviderProps) { + // Bundle "en" messages with the app + const [i18n] = useState(() => new I18n({ locale: 'en', messages: { en } })) + + const [desiredLocale, setDesiredLocale] = useState(() => + detectLocale( + knownLocales.filter( + (l) => !availableLocales || availableLocales.includes(l), + ), + ), + ) + const [currentLocale, setCurrentLocale] = useState(() => i18n.locale) + + const [loaded, setLoaded] = useState(desiredLocale === currentLocale) + + // Keep currentLocale in sync with i18n + useEffect(() => { + const onChange = () => { + setCurrentLocale(i18n.locale) + document.documentElement.setAttribute('lang', i18n.locale) + } + i18n.on('change', onChange) + return () => i18n.removeListener('change', onChange) + }, [i18n]) + + useEffect(() => { + if (currentLocale === desiredLocale) { + setLoaded(true) + return + } + + let canceled = false + loadMessages(desiredLocale) + .then((messages) => { + i18n.load(desiredLocale, messages) + if (!canceled) i18n.activate(desiredLocale) + }) + .catch((err) => { + console.error(`Failed to load locale "${desiredLocale}":`, err) + }) + .finally(() => { + if (!canceled) setLoaded(true) + }) + return () => { + canceled = true + } + }, [currentLocale, desiredLocale]) + + const value = useMemo( + () => ({ + locale: currentLocale, + locales: Object.fromEntries( + knownLocales + .filter((l) => !availableLocales || availableLocales.includes(l)) + .map((l) => [l, locales[l]]), + ), + setLocale: setDesiredLocale, + }), + [currentLocale, availableLocales, locales, setDesiredLocale], + ) + + return ( + + {loaded && children} + + ) +} + +function detectLocale( + availableLocales: readonly L[], + fallbackLocale: L | 'en' = 'en', +): L { + // Use, in priority, the locale that was set by the backend + if (typeof document === 'object') { + const htmlLang = document.documentElement.getAttribute('lang') + const resolved = htmlLang && resolveLocale(htmlLang, availableLocales) + if (resolved) return resolved + } + + // Should that fail (though it should probably never), negotiate with the browser + if (typeof navigator === 'object' && navigator.languages) { + for (const locale of navigator.languages) { + const resolved = resolveLocale(locale, availableLocales) + if (resolved) return resolved + } + } + + const fallback = resolveLocale(fallbackLocale, availableLocales) + if (fallback) return fallback + + // Type-safety + throw new TypeError( + `Available locales should always contain "${fallbackLocale}"`, + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/locale-selector.tsx b/packages/oauth/oauth-provider/src/assets/app/locales/locale-selector.tsx new file mode 100644 index 000000000..9de7d18f0 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/locale-selector.tsx @@ -0,0 +1,58 @@ +import { useLingui } from '@lingui/react/macro' +import { JSX } from 'react' +import { clsx } from '../lib/clsx.ts' +import { useLocaleContext } from './locale-context.ts' + +export type LocaleSelectorProps = Omit< + JSX.IntrinsicElements['select'], + 'value' | 'defaultValue' +> + +export function LocaleSelector({ + className, + onChange, + ...props +}: LocaleSelectorProps) { + const { locale, locales, setLocale } = useLocaleContext() + const { t } = useLingui() + + return ( + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/locales.ts b/packages/oauth/oauth-provider/src/assets/app/locales/locales.ts new file mode 100644 index 000000000..9846ef474 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/locales.ts @@ -0,0 +1,168 @@ +// @TODO Enable locales once they get translated +export const locales = { + an: { + name: 'Aragonés', + }, + ast: { + name: 'Asturianu', + }, + ca: { + name: 'Català', + flag: '🇦🇩', // Andorra's flag (though Andorra does not cover the entire zone speaking Català) + }, + da: { + name: 'Dansk', + flag: '🇩🇰', + }, + de: { + name: 'Deutsch', + flag: '🇩🇪', + }, + el: { + name: 'Ελληνικά', + flag: '🇬🇷', + }, + en: { + name: 'English', + flag: '🇺🇸', + }, + 'en-GB': { + name: 'English (UK)', + flag: '🇬🇧', + }, + es: { + name: 'Español', + flag: '🇪🇸', + }, + eu: { + name: 'Euskara', + }, + fi: { + name: 'Suomi', + flag: '🇫🇮', + }, + fr: { + name: 'Français', + flag: '🇫🇷', + }, + ga: { + name: 'Gaeilge', + flag: '🇮🇪', + }, + gl: { + name: 'Galego', + }, + hi: { + name: 'हिन्दी', + flag: '🇮🇳', + }, + hu: { + name: 'Magyar', + flag: '🇭🇺', + }, + ia: { + name: 'Interlingua', + }, + id: { + name: 'Bahasa Indonesia', + flag: '🇮🇩', + }, + it: { + name: 'Italiano', + flag: '🇮🇹', + }, + ja: { + name: '日本語', + flag: '🇯🇵', + }, + km: { + name: 'ភាសាខ្មែរ', + flag: '🇰🇭', + }, + ko: { + name: '한국어', + flag: '🇰🇷', + }, + ne: { + name: 'नेपाली', + flag: '🇳🇵', + }, + nl: { + name: 'Nederlands', + flag: '🇳🇱', + }, + pl: { + name: 'Polski', + flag: '🇵🇱', + }, + 'pt-BR': { + name: 'Português (Brasil)', + flag: '🇧🇷', + }, + ro: { + name: 'Română', + flag: '🇷🇴', + }, + ru: { + name: 'Русский', + flag: '🇷🇺', + }, + sv: { + name: 'Svenska', + flag: '🇸🇪', + }, + th: { + name: 'ไทย', + flag: '🇹🇭', + }, + tr: { + name: 'Türkçe', + flag: '🇹🇷', + }, + uk: { + name: 'Українська', + flag: '🇺🇦', + }, + vi: { + name: 'Tiếng Việt', + flag: '🇻🇳', + }, + 'zh-CN': { + name: '中文(简体)', + flag: '🇨🇳', + }, + 'zh-HK': { + name: '中文(香港)', + flag: '🇭🇰', + }, + 'zh-TW': { + name: '中文(繁體)', + flag: '🇹🇼', + }, +} as const satisfies Record + +export const knownLocales = Object.keys(locales) as readonly KnownLocale[] +export type KnownLocale = keyof typeof locales +export const isKnownLocale = (v: unknown): v is KnownLocale => + (knownLocales as readonly unknown[]).includes(v) + +export function resolveLocale( + locale: string, + availableLocales: readonly L[], +): L | undefined { + if ((availableLocales as readonly string[]).includes(locale)) { + return locale as L + } + + const lang = locale.split('-')[0] + if ((availableLocales as readonly string[]).includes(lang)) { + return lang as L + } + + const similar = availableLocales.find((l) => l.startsWith(`${lang}-`)) + if (similar) { + return similar as L + } + + return undefined +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ne/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ne/messages.po new file mode 100644 index 000000000..af34514ba --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ne/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: ne\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/nl/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/nl/messages.po new file mode 100644 index 000000000..b7c6f1bf9 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/nl/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: nl\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/pl/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/pl/messages.po new file mode 100644 index 000000000..0869730af --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/pl/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: pl\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/pt-BR/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/pt-BR/messages.po new file mode 100644 index 000000000..935b74aa5 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/pt-BR/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: pt-BR\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ro/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ro/messages.po new file mode 100644 index 000000000..eb50db8fc --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ro/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: ro\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ru/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ru/messages.po new file mode 100644 index 000000000..9969d0857 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ru/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: ru\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/sv/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/sv/messages.po new file mode 100644 index 000000000..8001da8b3 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/sv/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: sv\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/th/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/th/messages.po new file mode 100644 index 000000000..7f8cb174b --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/th/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: th\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/tr/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/tr/messages.po new file mode 100644 index 000000000..aa026bf29 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/tr/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: tr\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/uk/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/uk/messages.po new file mode 100644 index 000000000..79d8544f4 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/uk/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: uk\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/vi/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/vi/messages.po new file mode 100644 index 000000000..6d8a76c7e --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/vi/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: vi\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/zh-CN/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/zh-CN/messages.po new file mode 100644 index 000000000..094a9c643 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/zh-CN/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: zh-CN\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/zh-HK/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/zh-HK/messages.po new file mode 100644 index 000000000..53c9c52a7 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/zh-HK/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: zh-HK\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/zh-TW/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/zh-TW/messages.po new file mode 100644 index 000000000..aec7ce5d4 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/zh-TW/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\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: zh-TW\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +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 "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/main.css b/packages/oauth/oauth-provider/src/assets/app/main.css index c6fb6868c..b84e6b219 100644 --- a/packages/oauth/oauth-provider/src/assets/app/main.css +++ b/packages/oauth/oauth-provider/src/assets/app/main.css @@ -2,11 +2,32 @@ @tailwind components; @tailwind utilities; -/* Matches colors defined in tailwind.config.js */ +/* + * These are the default branding variables for the oauth authorization page. + * The colors below are in the R G B format, with values ranging from 0 to 255. + * + * The variables defined here are typically overridden by the backend, based on + * the provided branding colors configuration, if any, by injecting a ` // hash validity requires no space around the content - : html`` + : html`` } diff --git a/packages/oauth/oauth-provider/src/lib/http/middleware.ts b/packages/oauth/oauth-provider/src/lib/http/middleware.ts index b052f0ef4..515266363 100644 --- a/packages/oauth/oauth-provider/src/lib/http/middleware.ts +++ b/packages/oauth/oauth-provider/src/lib/http/middleware.ts @@ -2,6 +2,8 @@ import type { IncomingMessage, ServerResponse } from 'node:http' import { writeJson } from './response.js' import { Handler, Middleware, NextFunction } from './types.js' +const isNonNullable = (x: X): x is NonNullable => x != null + export function combineMiddlewares>( middlewares: Iterable, options?: { skipKeyword?: string }, @@ -15,12 +17,11 @@ export function combineMiddlewares( middlewares: Iterable>, { skipKeyword }: { skipKeyword?: string } = {}, ): Middleware { - const middlewaresArray = Array.from(middlewares).filter( - (x): x is NonNullable => x != null, - ) + const middlewaresArray = Array.from(middlewares).filter(isNonNullable) // Optimization: if there are no middlewares, return a noop middleware. if (middlewaresArray.length === 0) return (req, res, next) => void next() + if (middlewaresArray.length === 1) return middlewaresArray[0] return function (req, res, next) { let i = 0 diff --git a/packages/oauth/oauth-provider/src/lib/http/request.ts b/packages/oauth/oauth-provider/src/lib/http/request.ts index 4479c2ef2..2f4b9f630 100644 --- a/packages/oauth/oauth-provider/src/lib/http/request.ts +++ b/packages/oauth/oauth-provider/src/lib/http/request.ts @@ -1,5 +1,6 @@ import { randomBytes } from 'node:crypto' import type { IncomingMessage, ServerResponse } from 'node:http' +import { languages, mediaType } from '@hapi/accept' import { parse as parseCookie, serialize as serializeCookie } from 'cookie' import forwarded from 'forwarded' import createHttpError from 'http-errors' @@ -79,6 +80,18 @@ export function validateFetchSite( validateHeaderValue(req, 'sec-fetch-site', expectedSite) } +export function validateReferer( + req: IncomingMessage, + res: ServerResponse, + reference: UrlReference, + allowNull: true, +): URL | null +export function validateReferer( + req: IncomingMessage, + res: ServerResponse, + reference: UrlReference, + allowNull?: false, +): URL export function validateReferer( req: IncomingMessage, res: ServerResponse, @@ -90,6 +103,7 @@ export function validateReferer( if (refererUrl ? !urlMatch(refererUrl, reference) : !allowNull) { throw createHttpError(400, `Invalid referer ${referer}`) } + return refererUrl } export async function setupCsrfToken( @@ -126,12 +140,13 @@ export function validateSameOrigin( export function validateCsrfToken( req: IncomingMessage, res: ServerResponse, - csrfToken: string, + csrfToken: unknown, cookieName = 'csrf_token', clearCookie = false, ) { const cookies = parseHttpCookies(req) if ( + typeof csrfToken !== 'string' || !csrfToken || !cookies || !cookieName || @@ -248,3 +263,18 @@ function extractPort(req: IncomingMessage, ip: string): number { throw new Error('Could not determine port') } + +export function extractLocales(req: IncomingMessage) { + const acceptLanguage = req.headers['accept-language'] + return acceptLanguage ? languages(acceptLanguage) : [] +} + +export function negotiateResponseContent( + req: IncomingMessage, + types: readonly T[], +): T | undefined { + const type = mediaType(req.headers['accept'], types) + if (type) return type as T + + return undefined +} diff --git a/packages/oauth/oauth-provider/src/lib/http/response.ts b/packages/oauth/oauth-provider/src/lib/http/response.ts index 12ba2627a..32ab8b7e4 100644 --- a/packages/oauth/oauth-provider/src/lib/http/response.ts +++ b/packages/oauth/oauth-provider/src/lib/http/response.ts @@ -1,6 +1,6 @@ import type { ServerResponse } from 'node:http' import { type Readable, pipeline } from 'node:stream' -import { Handler } from './types.js' +import type { Handler, Middleware } from './types.js' export function appendHeader( res: ServerResponse, @@ -53,22 +53,27 @@ export function writeStream( export function writeBuffer( res: ServerResponse, chunk: string | Buffer, - { - status = 200, - contentType = 'application/octet-stream', - }: WriteResponseOptions = {}, + opts: WriteResponseOptions, ): void { - res.statusCode = status - res.setHeader('content-type', contentType) + if (opts?.status != null) res.statusCode = opts.status + res.setHeader('content-type', opts?.contentType || 'application/octet-stream') res.end(chunk) } +export function toJsonBuffer(value: unknown): Buffer { + try { + return Buffer.from(JSON.stringify(value)) + } catch (cause) { + throw new Error(`Failed to serialize as JSON`, { cause }) + } +} + export function writeJson( res: ServerResponse, payload: unknown, { contentType = 'application/json', ...options }: WriteResponseOptions = {}, ): void { - const buffer = Buffer.from(JSON.stringify(payload)) + const buffer = toJsonBuffer(payload) writeBuffer(res, buffer, { ...options, contentType }) } @@ -76,7 +81,7 @@ export function staticJsonMiddleware( value: unknown, { contentType = 'application/json', ...options }: WriteResponseOptions = {}, ): Handler { - const buffer = Buffer.from(JSON.stringify(value)) + const buffer = toJsonBuffer(value) const staticOptions: WriteResponseOptions = { ...options, contentType } return function (req, res) { writeBuffer(res, buffer, staticOptions) @@ -90,3 +95,11 @@ export function writeHtml( ): void { writeBuffer(res, html, { ...options, contentType }) } + +export function cacheControlMiddleware(maxAge: number): Middleware { + const header = `max-age=${maxAge}` + return function (req, res, next) { + res.setHeader('Cache-Control', header) + next() + } +} diff --git a/packages/oauth/oauth-provider/src/lib/locale.ts b/packages/oauth/oauth-provider/src/lib/locale.ts new file mode 100644 index 000000000..877a40542 --- /dev/null +++ b/packages/oauth/oauth-provider/src/lib/locale.ts @@ -0,0 +1,21 @@ +import { z } from 'zod' + +export const localeSchema = z + .string() + .regex(/^[a-z]{2,3}(-[A-Z]{2})?$/, 'Invalid locale') +export type Locale = z.infer + +export const multiLangStringSchema = z.intersection( + z.object({ en: z.string() }), // en is required + z.record(localeSchema, z.union([z.string(), z.undefined()])), +) +export type MultiLangString = z.infer + +export const AVAILABLE_LOCALES = [ + // TODO: Add more in this list as translations are added in the PO files + 'en', + 'fr', +] as const satisfies readonly Locale[] +export type AvailableLocale = (typeof AVAILABLE_LOCALES)[number] +export const isAvailableLocale = (v: unknown): v is AvailableLocale => + (AVAILABLE_LOCALES as readonly unknown[]).includes(v) diff --git a/packages/oauth/oauth-provider/src/lib/util/function.ts b/packages/oauth/oauth-provider/src/lib/util/function.ts index e755ebc4b..f74f90819 100644 --- a/packages/oauth/oauth-provider/src/lib/util/function.ts +++ b/packages/oauth/oauth-provider/src/lib/util/function.ts @@ -6,17 +6,14 @@ * particularly useful when the function is a member of a "private" object. */ export async function callAsync unknown>( - this: ThisParameterType, fn: F, ...args: Parameters ): Promise>> export async function callAsync unknown>( - this: ThisParameterType, fn?: F, ...args: Parameters ): Promise> | undefined> export async function callAsync unknown>( - this: ThisParameterType, fn?: F, ...args: Parameters ): Promise> | undefined> { diff --git a/packages/oauth/oauth-provider/src/lib/util/type.ts b/packages/oauth/oauth-provider/src/lib/util/type.ts index 735e0b585..6a3451595 100644 --- a/packages/oauth/oauth-provider/src/lib/util/type.ts +++ b/packages/oauth/oauth-provider/src/lib/util/type.ts @@ -1,4 +1,133 @@ // eslint-disable-next-line @typescript-eslint/ban-types export type Simplify = { [K in keyof T]: T[K] } & {} -export type Override = Simplify> +export type Override = Simplify<{ + [K in keyof (V & T)]: K extends keyof V + ? V[K] + : K extends keyof T + ? T[K] + : never +}> export type Awaitable = T | Promise + +/** + * Similar to {@link Required} but also ensures that all values are defined. + */ +export type RequiredDefined = { [K in keyof T]-?: Exclude } + +// (don't touch this) + +/** + * @example + * ```ts + * type F = UnionToFnUnion<'a' | 'b'> // (() => 'a') | (() => 'b') + * ``` + */ +type UnionToFnUnion = T extends any ? () => T : never + +/** + * @example + * ```ts + * type A = UnionToIntersection<(() => 'a') | (() => 'b')> // (() => 'a') & (() => 'b') + * + * UnionToIntersection<{ foo: string | number } | { foo: number; bar: 4 }> // { foo: number; bar: 4 } + * ``` + */ +type UnionToIntersection = (T extends any ? (x: T) => void : never) extends ( + x: infer U, +) => void + ? U + : never + +/** + * @example + * ```ts + * type B = ExtractUnionItem<'a' | 'b'> // 'b' + * ``` + */ +type ExtractUnionItem = + // There exists a quirk in the way TypeScript works when inferring return + // types of an (disjoined) intersection of functions: + // + // type AnB = (() => 'a') & (() => 'b') + // type B = AnB extends () => infer R ? R : never // 'b' + // + // By turning the input union T (e.g. 'a' | 'b') into a union of function + // (() => 'a') | (() => 'b') and then into an intersection of those functions + // (() => 'a') & (() => 'b'), we can exploit the special TypeScript behavior + // to infer only the last return type from the functions, which is effectively + // equal to the last item of the input union T. + UnionToIntersection> extends () => infer R ? R : never + +/** + * Utility that turn a union of types (`'a' | 'b'`) into a tuple with matching + * types (`['a', 'b']`). + * + * @note this only work with unions of "const" types. Using this with globals + * types (`string`, etc.) will yield unexpected results. + * + * @example + * ```ts + * type T = UnionToTuple<'a' | 'b'> // ['a', 'b'] + * type T = UnionToTuple<'a' | 'b' | 'c'> // ['a', 'b', 'c'] + * ``` + */ +type UnionToTuple = UnionToTupleInternal + +type UnionToTupleInternal< + T, + // Accumulator for terminal recursivity (initialized to empty tuple) + Acc extends readonly any[] = [], + // Get the next item from the union (if any) + Next = ExtractUnionItem, +> = + // If there were no more items to extract from the union T, then we are done + [Next] extends [never] + ? // Return result of previous recursive calls + Acc + : // Recursively call UnionToTupleInternal by Exclude'ing the Next item from + // the union (T) and adding it to the accumulator. + UnionToTupleInternal, readonly [Next, ...Acc]> + +/** + * This utility allows to create an assertion function that checks if a + * particular interface is fully implemented by some value. + * + * The use of the (rather complex) {@link UnionToTuple} allows to ensure that, + * at runtime, all the required interface keys are indeed checked, and that + * future additions to the interface do not result in a false sense of type + * safety. + * + * @note This function should not be made public, as it relies on a quirk of + * TypeScript to work properly. + * + * @example Valid use + * + * ```ts + * const isFoo = buildInterfaceChecker<{ foo: string }>(['foo']) + * const isFooBar = buildInterfaceChecker<{ foo: string; bar: boolean }>([ + * 'foo', + * 'bar', + * ]) + * + * declare const val: { foo?: string } + * + * if (isFoo(val)) { + * val // { foo: string } + * } + * ``` + * + * @example Use cases where the runtime keys do not match the interface keys + * + * ```ts + * buildInterfaceChecker<{ foo: string }>([]) + * buildInterfaceChecker<{ foo: string }>(['fee']) + * buildInterfaceChecker<{ foo: string; bar: string }>(['foo']) + * buildInterfaceChecker<{ foo: string; bar: string }>(['foo', 'baz']) + * ``` + */ +export const buildInterfaceChecker = + (keys: readonly string[] & UnionToTuple) => + >(value: V): value is V & RequiredDefined => + keys.every((name) => value[name] !== undefined) + +// diff --git a/packages/oauth/oauth-provider/src/metadata/build-metadata.ts b/packages/oauth/oauth-provider/src/metadata/build-metadata.ts index 9a9172fd1..e67ea3fe8 100644 --- a/packages/oauth/oauth-provider/src/metadata/build-metadata.ts +++ b/packages/oauth/oauth-provider/src/metadata/build-metadata.ts @@ -1,6 +1,7 @@ import { Keyset } from '@atproto/jwk' import { OAuthAuthorizationServerMetadata, + OAuthIssuerIdentifier, oauthAuthorizationServerMetadataSchema, } from '@atproto/oauth-types' import { Client } from '../client/client.js' @@ -17,7 +18,7 @@ export type CustomMetadata = { * @see {@link https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata} */ export function buildMetadata( - issuer: string, + issuer: OAuthIssuerIdentifier, keyset: Keyset, customMetadata?: CustomMetadata, ): OAuthAuthorizationServerMetadata { diff --git a/packages/oauth/oauth-provider/src/oauth-errors.ts b/packages/oauth/oauth-provider/src/oauth-errors.ts index f53eabac0..97a303421 100644 --- a/packages/oauth/oauth-provider/src/oauth-errors.ts +++ b/packages/oauth/oauth-provider/src/oauth-errors.ts @@ -4,6 +4,7 @@ export { OAuthError } from './errors/oauth-error.js' export { AccessDeniedError } from './errors/access-denied-error.js' export { AccountSelectionRequiredError } from './errors/account-selection-required-error.js' export { ConsentRequiredError } from './errors/consent-required-error.js' +export { HandleUnavailableError } from './errors/handle-unavailable-error.js' export { InvalidAuthorizationDetailsError } from './errors/invalid-authorization-details-error.js' export { InvalidClientError } from './errors/invalid-client-error.js' export { InvalidClientIdError } from './errors/invalid-client-id-error.js' diff --git a/packages/oauth/oauth-provider/src/oauth-hooks.ts b/packages/oauth/oauth-provider/src/oauth-hooks.ts index b5cf3784f..56af40b75 100644 --- a/packages/oauth/oauth-provider/src/oauth-hooks.ts +++ b/packages/oauth/oauth-provider/src/oauth-hooks.ts @@ -6,15 +6,18 @@ import { OAuthTokenResponse, } from '@atproto/oauth-types' import { Account } from './account/account.js' +import { SignInData } from './account/sign-in-data.js' +import { SignUpData } from './account/sign-up-data.js' import { ClientAuth } from './client/client-auth.js' import { ClientId } from './client/client-id.js' import { ClientInfo } from './client/client-info.js' import { Client } from './client/client.js' -import { InvalidAuthorizationDetailsError } from './errors/invalid-authorization-details-error.js' +import { InvalidRequestError } from './errors/invalid-request-error.js' +import { HcaptchaConfig, HcaptchaVerifyResult } from './lib/hcaptcha.js' import { RequestMetadata } from './lib/http/request.js' import { Awaitable } from './lib/util/type.js' import { AccessDeniedError, OAuthError } from './oauth-errors.js' -import { DeviceId } from './oauth-store.js' +import { DeviceAccountInfo, DeviceId } from './oauth-store.js' // Make sure all types needed to implement the OAuthHooks are exported export { @@ -25,8 +28,11 @@ export { type ClientAuth, type ClientId, type ClientInfo, + type DeviceAccountInfo, type DeviceId, - InvalidAuthorizationDetailsError, + type HcaptchaConfig, + type HcaptchaVerifyResult, + InvalidRequestError, type Jwks, type OAuthAuthorizationDetails, type OAuthAuthorizationRequestParameters, @@ -34,6 +40,8 @@ export { OAuthError, type OAuthTokenResponse, type RequestMetadata, + type SignInData, + type SignUpData, } export type OAuthHooks = { @@ -63,6 +71,64 @@ export type OAuthHooks = { account: Account }) => Awaitable + /** + * This hook is called whenever an hcaptcha challenge is verified + * during sign-up (if hcaptcha is enabled). + * + * @throws {InvalidRequestError} to deny the sign-up + */ + onSignupHcaptchaResult?: (data: { + data: SignUpData + /** + * This indicates not only wether the hCaptcha challenge succeeded, but also + * if the score was low enough according to the + * {@link HcaptchaConfig.scoreThreshold}. + * + * @see {@link HCaptchaClient.isAllowed} + */ + allowed: boolean + result: HcaptchaVerifyResult + deviceId: DeviceId + deviceMetadata: RequestMetadata + }) => Awaitable + + /** + * This hook is called when a user attempts to sign up, after every validation + * has passed (including hcaptcha). + */ + onSignupAttempt?: (data: { + data: SignUpData + deviceId: DeviceId + deviceMetadata: RequestMetadata + hcaptchaResult?: HcaptchaVerifyResult + }) => Awaitable + + /** + * This hook is called when a user successfully signs up. + * + * @throws {AccessDeniedError} to deny the sign-up + */ + onSignedUp?: (data: { + data: SignUpData + info: DeviceAccountInfo + account: Account + deviceId: DeviceId + deviceMetadata: RequestMetadata + }) => Awaitable + + /** + * This hook is called when a user successfully signs in. + * + * @throws {InvalidRequestError} when the sing-in should be denied + */ + onSignedIn?: (data: { + data: SignInData + info: DeviceAccountInfo + account: Account + deviceId: DeviceId + deviceMetadata: RequestMetadata + }) => Awaitable + /** * This hook is called when a client is authorized. * diff --git a/packages/oauth/oauth-provider/src/oauth-provider.ts b/packages/oauth/oauth-provider/src/oauth-provider.ts index 2147b3998..387b22b75 100644 --- a/packages/oauth/oauth-provider/src/oauth-provider.ts +++ b/packages/oauth/oauth-provider/src/oauth-provider.ts @@ -1,5 +1,4 @@ import type { IncomingMessage, ServerResponse } from 'node:http' -import { mediaType } from '@hapi/accept' import createHttpError from 'http-errors' import type { Redis, RedisOptions } from 'ioredis' import { ZodError, z } from 'zod' @@ -39,14 +38,16 @@ import { AccountManager } from './account/account-manager.js' import { AccountStore, DeviceAccountInfo, - SignInCredentials, asAccountStore, - signInCredentialsSchema, + handleSchema, + resetPasswordConfirmDataSchema, + resetPasswordRequestDataSchema, } from './account/account-store.js' import { Account } from './account/account.js' +import { signInDataSchema } from './account/sign-in-data.js' +import { signUpDataSchema } from './account/sign-up-data.js' import { authorizeAssetsMiddleware } from './assets/assets-middleware.js' import { ClientAuth, authJwkThumbprint } from './client/client-auth.js' -import { ClientId, clientIdSchema } from './client/client-id.js' import { ClientManager, LoopbackMetadataGetter, @@ -55,7 +56,12 @@ import { ClientStore, ifClientStore } from './client/client-store.js' import { Client } from './client/client.js' import { AUTHENTICATION_MAX_AGE, TOKEN_MAX_AGE } from './constants.js' import { DeviceId } from './device/device-id.js' -import { DeviceManager, DeviceManagerOptions } from './device/device-manager.js' +import { + DeviceInfo, + DeviceManager, + DeviceManagerOptions, + deviceManagerOptionsSchema, +} from './device/device-manager.js' import { DeviceStore, asDeviceStore } from './device/device-store.js' import { AccessDeniedError } from './errors/access-denied-error.js' import { AccountSelectionRequiredError } from './errors/account-selection-required-error.js' @@ -65,13 +71,14 @@ import { InvalidGrantError } from './errors/invalid-grant-error.js' import { InvalidParametersError } from './errors/invalid-parameters-error.js' import { InvalidRequestError } from './errors/invalid-request-error.js' import { LoginRequiredError } from './errors/login-required-error.js' -import { OAuthError } from './errors/oauth-error.js' import { UnauthorizedClientError } from './errors/unauthorized-client-error.js' import { WWWAuthenticateError } from './errors/www-authenticate-error.js' +import { HcaptchaConfig } from './lib/hcaptcha.js' import { Handler, Middleware, Router, + cacheControlMiddleware, combineMiddlewares, parseHttpRequest, setupCsrfToken, @@ -84,18 +91,26 @@ import { validateSameOrigin, writeJson, } from './lib/http/index.js' -import { RequestMetadata } from './lib/http/request.js' +import { + RequestMetadata, + extractLocales, + negotiateResponseContent as negotiateContent, +} from './lib/http/request.js' import { dateToEpoch, dateToRelativeSeconds } from './lib/util/date.js' -import { Override } from './lib/util/type.js' +import { Awaitable, Override } from './lib/util/type.js' import { CustomMetadata, buildMetadata } from './metadata/build-metadata.js' -import { OAuthHooks } from './oauth-hooks.js' +import { OAuthHooks, SignInData, SignUpData } from './oauth-hooks.js' import { OAuthVerifier, OAuthVerifierOptions } from './oauth-verifier.js' import { AuthorizationResultAuthorize } from './output/build-authorize-data.js' +import { + BrandingConfig, + Customization, + customizationSchema, +} from './output/build-customization-data.js' import { buildErrorPayload, buildErrorStatus, } from './output/build-error-payload.js' -import { Customization } from './output/customization.js' import { OutputManager } from './output/output-manager.js' import { AuthorizationResultRedirect, @@ -114,32 +129,36 @@ import { TokenManager } from './token/token-manager.js' import { TokenStore, asTokenStore } from './token/token-store.js' import { VerifyTokenClaimsOptions } from './token/verify-token-claims.js' -export type OAuthProviderStore = Partial< - ClientStore & - AccountStore & - DeviceStore & - TokenStore & - RequestStore & - ReplayStore -> - export { + type BrandingConfig, type CustomMetadata, type Customization, type Handler, + type HcaptchaConfig, Keyset, type OAuthAuthorizationServerMetadata, } +type ApiContext = { + requestUri: RequestUri + deviceId: DeviceId + deviceMetadata: RequestMetadata +} + +export type ErrorHandler< + Req extends IncomingMessage = IncomingMessage, + Res extends ServerResponse = ServerResponse, +> = (req: Req, res: Res, err: unknown, message: string) => void + export type RouterOptions< Req extends IncomingMessage = IncomingMessage, Res extends ServerResponse = ServerResponse, > = { - onError?: (req: Req, res: Res, err: unknown, message: string) => void + onError?: ErrorHandler } export type OAuthProviderOptions = Override< - OAuthVerifierOptions & OAuthHooks & DeviceManagerOptions, + OAuthVerifierOptions & OAuthHooks & DeviceManagerOptions & Customization, { /** * Maximum age a device/account session can be before requiring @@ -157,11 +176,6 @@ export type OAuthProviderOptions = Override< */ metadata?: CustomMetadata - /** - * UI customizations - */ - customization?: Customization - /** * A custom fetch function that can be used to fetch the client metadata from * the internet. By default, the fetch function is a safeFetchWrap() function @@ -184,11 +198,18 @@ export type OAuthProviderOptions = Override< * this store implements all the interfaces not provided in the other * `Store` options. */ - store?: OAuthProviderStore + store?: Partial< + AccountStore & + ClientStore & + DeviceStore & + ReplayStore & + RequestStore & + TokenStore + > accountStore?: AccountStore - deviceStore?: DeviceStore clientStore?: ClientStore + deviceStore?: DeviceStore replayStore?: ReplayStore requestStore?: RequestStore tokenStore?: TokenStore @@ -235,7 +256,6 @@ export class OAuthProvider extends OAuthVerifier { public constructor({ metadata, - customization = undefined, authenticationMaxAge = AUTHENTICATION_MAX_AGE, tokenMaxAge = TOKEN_MAX_AGE, @@ -264,10 +284,30 @@ export class OAuthProvider extends OAuthVerifier { loopbackMetadata = atprotoLoopbackClientMetadata, - // OAuthHooks & OAuthVerifierOptions & DeviceManagerOptions + // OAuthHooks & + // OAuthVerifierOptions & + // DeviceManagerOptions & + // Customization ...rest }: OAuthProviderOptions) { - super({ replayStore, redis, ...rest }) + const customization: Customization = customizationSchema.parse(rest) + const deviceManagerOptions: DeviceManagerOptions = + deviceManagerOptionsSchema.parse(rest) + + // @NOTE: hooks don't really need a type parser, as all zod can actually + // check at runtime is the fact that the values are functions. The only way + // we would benefit from zod here would be to wrap the functions with a + // validator for the provided function's return types, which we do not add + // because it would impact runtime performance and we trust the users of + // this lib (basically ourselves) to rely on the typing system to ensure the + // correct types are returned. + const hooks: OAuthHooks = rest + + // @NOTE: validation of super params (if we wanted to implement it) should + // be the responsibility of the super class. + const superOptions: OAuthVerifierOptions = rest + + super({ replayStore, redis, ...superOptions }) requestStore ??= redis ? new RequestStoreRedis({ redis }) @@ -276,13 +316,18 @@ export class OAuthProvider extends OAuthVerifier { this.authenticationMaxAge = authenticationMaxAge this.metadata = buildMetadata(this.issuer, this.keyset, metadata) - this.deviceManager = new DeviceManager(deviceStore, rest) + this.deviceManager = new DeviceManager(deviceStore, deviceManagerOptions) this.outputManager = new OutputManager(customization) - this.accountManager = new AccountManager(accountStore) + this.accountManager = new AccountManager( + this.issuer, + accountStore, + hooks, + customization, + ) this.clientManager = new ClientManager( this.metadata, this.keyset, - rest, + hooks, clientStore || null, loopbackMetadata || null, safeFetch, @@ -293,12 +338,12 @@ export class OAuthProvider extends OAuthVerifier { requestStore, this.signer, this.metadata, - rest, + hooks, ) this.tokenManager = new TokenManager( tokenStore, this.signer, - rest, + hooks, this.accessTokenType, tokenMaxAge, ) @@ -451,8 +496,7 @@ export class OAuthProvider extends OAuthVerifier { // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3-1 // > Since initial processing of the pushed authorization request does not // > involve resource owner interaction, error codes related to user - // > interaction, such as consent_required defined by [OIDC], are never - // > returned. + // > interaction, such as "access_denied", are never returned. if (err instanceof AccessDeniedError) { throw new InvalidRequestError(err.error_description, err) } @@ -470,7 +514,7 @@ export class OAuthProvider extends OAuthVerifier { .parseAsync(query.request_uri, { path: ['query', 'request_uri'] }) .catch(throwInvalidRequest) - return this.requestManager.get(requestUri, client.id, deviceId) + return this.requestManager.get(requestUri, deviceId, client.id) } if ('request' in query) { @@ -514,11 +558,11 @@ export class OAuthProvider extends OAuthVerifier { } private async deleteRequest( - uri: RequestUri, + requestUri: RequestUri, parameters: OAuthAuthorizationRequestParameters, ) { try { - await this.requestManager.delete(uri) + await this.requestManager.delete(requestUri) } catch (err) { throw AccessDeniedError.from(parameters, err) } @@ -690,24 +734,46 @@ export class OAuthProvider extends OAuthVerifier { })) } - protected async signIn( - deviceId: DeviceId, - uri: RequestUri, - clientId: ClientId, - credentials: SignInCredentials, + protected async signUp( + { requestUri, deviceId, deviceMetadata }: ApiContext, + data: SignUpData, ): Promise<{ account: Account consentRequired: boolean }> { + const { clientId } = await this.requestManager.get(requestUri, deviceId) + const client = await this.clientManager.getClient(clientId) + const { account } = await this.accountManager.signUp( + data, + deviceId, + deviceMetadata, + ) + + return { + account, + consentRequired: !client.info.isFirstParty, + } + } + + protected async signIn( + { requestUri, deviceId, deviceMetadata }: ApiContext, + data: SignInData, + ): Promise<{ + account: Account + consentRequired: boolean + }> { // Ensure the request is still valid (and update the request expiration) // @TODO use the returned scopes to determine if consent is required - await this.requestManager.get(uri, clientId, deviceId) + const { clientId } = await this.requestManager.get(requestUri, deviceId) + + const client = await this.clientManager.getClient(clientId) const { account, info } = await this.accountManager.signIn( - credentials, + data, deviceId, + deviceMetadata, ) return { @@ -722,22 +788,21 @@ export class OAuthProvider extends OAuthVerifier { } protected async acceptRequest( - uri: RequestUri, - clientId: ClientId, + { requestUri, deviceId, deviceMetadata }: ApiContext, sub: string, - deviceId: DeviceId, - deviceMetadata: RequestMetadata, ): Promise { const { issuer } = this - const client = await this.clientManager.getClient(clientId) - const { parameters, clientAuth } = await this.requestManager.get( - uri, - clientId, + const { parameters, clientId, clientAuth } = await this.requestManager.get( + requestUri, deviceId, ) + const client = await this.clientManager.getClient(clientId) + try { + // @TODO Currently, a user can "accept" a request for any did that sing-in + // on the device, even if "remember" was set to false. const { account, info } = await this.accountManager.get(deviceId, sub) // The user is trying to authorize without a fresh login @@ -749,7 +814,7 @@ export class OAuthProvider extends OAuthVerifier { } const code = await this.requestManager.setAuthorized( - uri, + requestUri, client, account, deviceId, @@ -765,24 +830,19 @@ export class OAuthProvider extends OAuthVerifier { return { issuer, parameters, redirect: { code } } } catch (err) { - await this.deleteRequest(uri, parameters) + await this.deleteRequest(requestUri, parameters) throw AccessDeniedError.from(parameters, err) } } - protected async rejectRequest( - deviceId: DeviceId, - uri: RequestUri, - clientId: ClientId, - ): Promise { - const { parameters } = await this.requestManager.get( - uri, - clientId, - deviceId, - ) + protected async rejectRequest({ + requestUri, + deviceId, + }: ApiContext): Promise { + const { parameters } = await this.requestManager.get(requestUri, deviceId) - await this.deleteRequest(uri, parameters) + await this.deleteRequest(requestUri, parameters) return { issuer: this.issuer, @@ -1033,57 +1093,67 @@ export class OAuthProvider extends OAuthVerifier { // Utils - const csrfCookie = (uri: RequestUri) => `csrf-${uri}` - const onError = + const csrfCookie = (requestUri: RequestUri) => `csrf-${requestUri}` + const onError: null | ErrorHandler = options?.onError ?? (process.env['NODE_ENV'] === 'development' - ? (req, res, err, msg): void => + ? (req, res, err, msg) => { console.error(`OAuthProvider error (${msg}):`, err) - : undefined) + } + : null) - /** - * Creates a middleware that will serve static JSON content. - */ - const staticJson = (json: unknown): Middleware => - combineMiddlewares([ - function (req, res, next) { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Headers', '*') + // CORS preflight + const corsHeaders: Middleware = function (req, res, next) { + res.setHeader('Access-Control-Max-Age', '86400') // 1 day - res.setHeader('Cache-Control', 'max-age=300') - next() - }, - staticJsonMiddleware(json), - ]) + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin + // + // > For requests without credentials, the literal value "*" can be + // > specified as a wildcard; the value tells browsers to allow + // > requesting code from any origin to access the resource. + // > Attempting to use the wildcard with credentials results in an + // > error. + // + // A "*" is safer to use than reflecting the request origin. + res.setHeader('Access-Control-Allow-Origin', '*') + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods + // > The value "*" only counts as a special wildcard value for + // > requests without credentials (requests without HTTP cookies or + // > HTTP authentication information). In requests with credentials, + // > it is treated as the literal method name "*" without special + // > semantics. + res.setHeader('Access-Control-Allow-Methods', '*') + + res.setHeader('Access-Control-Allow-Headers', 'Content-Type,DPoP') + + next() + } + + const corsPreflight: Middleware = combineMiddlewares([ + corsHeaders, + (req, res) => { + res.writeHead(200).end() + }, + ]) /** * Wrap an OAuth endpoint in a middleware that will set the appropriate * response headers and format the response as JSON. */ const jsonHandler = ( - buildJson: (this: T, req: TReq, res: TRes) => Json | Promise, + buildJson: (this: T, req: TReq, res: TRes) => Awaitable, status?: number, ): Handler => async function (req, res) { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Headers', '*') - - // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1 - res.setHeader('Cache-Control', 'no-store') - res.setHeader('Pragma', 'no-cache') - - // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2 - const dpopNonce = server.nextDpopNonce() - if (dpopNonce) { - const name = 'DPoP-Nonce' - res.setHeader(name, dpopNonce) - res.appendHeader('Access-Control-Expose-Headers', name) - } - try { + // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1 + res.setHeader('Cache-Control', 'no-store') + res.setHeader('Pragma', 'no-cache') + // Ensure we can agree on a content encoding & type before starting to // build the JSON response. - if (!mediaType(req.headers['accept'], ['application/json'])) { + if (!negotiateContent(req, ['application/json'])) { throw createHttpError(406, 'Unsupported media type') } @@ -1094,39 +1164,107 @@ export class OAuthProvider extends OAuthVerifier { res.writeHead(status ?? 204).end() } } catch (err) { - if (!res.headersSent) { - if (err instanceof WWWAuthenticateError) { - const name = 'WWW-Authenticate' - res.setHeader(name, err.wwwAuthenticateHeader) - res.appendHeader('Access-Control-Expose-Headers', name) - } + onError?.(req, res, err, 'OAuth request error') + if (!res.headersSent) { const payload = buildErrorPayload(err) const status = buildErrorStatus(err) writeJson(res, payload, { status }) } else { res.destroy() } - - // OAuthError are used to build expected responses, so we don't log - // them as errors. - if (!(err instanceof OAuthError) || err.statusCode >= 500) { - onError?.(req, res, err, 'Unexpected error') - } } } + const oauthHandler = ( + buildOAuthResponse: (this: T, req: TReq, res: TRes) => Awaitable, + status?: number, + ) => + combineMiddlewares([ + corsHeaders, + jsonHandler(async function (req, res) { + try { + // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2 + const dpopNonce = server.nextDpopNonce() + if (dpopNonce) { + const name = 'DPoP-Nonce' + res.setHeader(name, dpopNonce) + res.appendHeader('Access-Control-Expose-Headers', name) + } + + return await buildOAuthResponse.call(this, req, res) + } catch (err) { + if (!res.headersSent && err instanceof WWWAuthenticateError) { + const name = 'WWW-Authenticate' + res.setHeader(name, err.wwwAuthenticateHeader) + res.appendHeader('Access-Control-Expose-Headers', name) + } + + throw err + } + }, status), + ]) + + const apiHandler = < + T, + TReq extends Req, + TRes extends Res, + S extends z.ZodTypeAny, + Json, + >( + inputSchema: S, + buildJson: ( + this: T, + req: TReq, + res: TRes, + input: z.infer, + context: ApiContext, + ) => Json | Promise, + status?: number, + ) => + jsonHandler(async function (req, res) { + validateFetchMode(req, res, ['same-origin']) + validateFetchSite(req, res, ['same-origin']) + validateSameOrigin(req, res, issuerOrigin) + const referer = validateReferer(req, res, { + origin: issuerOrigin, + pathname: '/oauth/authorize', + }) + + const requestUri = await requestUriSchema.parseAsync( + referer.searchParams.get('request_uri'), + { path: ['query', 'request_uri'] }, + ) + + validateCsrfToken( + req, + res, + req.headers['x-csrf-token'], + csrfCookie(requestUri), + ) + + const { deviceId, deviceMetadata } = await server.deviceManager.load( + req, + res, + ) + + const payload = await parseHttpRequest(req, ['json']) + const input = await inputSchema.parseAsync(payload, { path: ['body'] }) + + const context: ApiContext = { requestUri, deviceId, deviceMetadata } + return buildJson.call(this, req, res, input, context) + }, status) + const navigationHandler = ( - handler: (this: T, req: TReq, res: TRes) => void | Promise, + handler: (this: T, req: TReq, res: TRes) => Awaitable, ): Handler => async function (req, res) { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Headers', '*') - - res.setHeader('Cache-Control', 'no-store') - res.setHeader('Pragma', 'no-cache') - try { + res.setHeader('Cache-Control', 'no-store') + res.setHeader('Pragma', 'no-cache') + + res.setHeader('Referrer-Policy', 'same-origin') + validateFetchMode(req, res, ['navigate']) validateFetchDest(req, res, ['document']) validateSameOrigin(req, res, issuerOrigin) @@ -1141,11 +1279,78 @@ export class OAuthProvider extends OAuthVerifier { ) if (!res.headersSent) { - await server.outputManager.sendErrorPage(res, err) + await server.outputManager.sendErrorPage(res, err, { + preferredLocales: extractLocales(req), + }) } } } + // Simple GET requests fall under the category of "no-cors" request, meaning + // that the browser will allow any cross-origin request, with credentials, + // to be sent to the oauth server. The OAuth Server will, however: + // 1) validate the request origin (see navigationHandler), + // 2) validate the CSRF token, + // 3) validate the referer, + // 4) validate the sec-fetch-site header, + // 4) validate the sec-fetch-mode header (see navigationHandler), + // 5) validate the sec-fetch-dest header (see navigationHandler). + // And will error (refuse to serve the request) if any of these checks fail. + const sameOriginNavigationHandler = < + T extends { url: URL }, + TReq extends Req, + TRes extends Res, + >( + handler: ( + this: T, + req: TReq, + res: TRes, + deviceInfo: DeviceInfo, + ) => Awaitable, + ): Handler => + navigationHandler(async function (req, res) { + validateFetchSite(req, res, ['same-origin']) + + const deviceInfo = await server.deviceManager.load(req, res) + + return handler.call(this, req, res, deviceInfo) + }) + + const authorizeRedirectNavigationHandler = < + T extends { url: URL }, + TReq extends Req, + TRes extends Res, + >( + handler: ( + this: T, + req: TReq, + res: TRes, + context: ApiContext, + ) => Awaitable, + ): Handler => + sameOriginNavigationHandler(async function (req, res, deviceInfo) { + const referer = validateReferer(req, res, { + origin: issuerOrigin, + pathname: '/oauth/authorize', + }) + + const requestUri = await requestUriSchema.parseAsync( + referer.searchParams.get('request_uri'), + ) + + const csrfToken = this.url.searchParams.get('csrf_token') + const csrfCookieName = csrfCookie(requestUri) + + // Next line will "clear" the CSRF token cookie, preventing replay of + // this request (navigating "back" will result in an error). + validateCsrfToken(req, res, csrfToken, csrfCookieName, true) + + const context: ApiContext = { ...deviceInfo, requestUri } + + const redirect = await handler.call(this, req, res, context) + return sendAuthorizeRedirect(res, redirect) + }) + /** * Provides a better UX when a request is denied by redirecting to the * client with the error details. This will also log any error that caused @@ -1172,44 +1377,26 @@ export class OAuthProvider extends OAuthVerifier { //- Public OAuth endpoints + router.options('/.well-known/oauth-authorization-server', corsPreflight) router.get( '/.well-known/oauth-authorization-server', - staticJson(server.metadata), + corsHeaders, + cacheControlMiddleware(300), + staticJsonMiddleware(server.metadata), ) - // CORS preflight - const corsPreflight: Middleware = function (req, res, _next) { - res - .writeHead(204, { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin - // - // > For requests without credentials, the literal value "*" can be - // > specified as a wildcard; the value tells browsers to allow - // > requesting code from any origin to access the resource. - // > Attempting to use the wildcard with credentials results in an - // > error. - // - // A "*" is safer to use than reflecting the request origin. - 'Access-Control-Allow-Origin': '*', - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods - // > The value "*" only counts as a special wildcard value for - // > requests without credentials (requests without HTTP cookies or - // > HTTP authentication information). In requests with credentials, - // > it is treated as the literal method name "*" without special - // > semantics. - 'Access-Control-Allow-Methods': '*', - 'Access-Control-Allow-Headers': 'Content-Type,Authorization,DPoP', - 'Access-Control-Max-Age': '86400', // 1 day - }) - .end() - } - - router.get('/oauth/jwks', staticJson(server.jwks)) + router.options('/oauth/jwks', corsPreflight) + router.get( + '/oauth/jwks', + corsHeaders, + cacheControlMiddleware(300), + staticJsonMiddleware(server.jwks), + ) router.options('/oauth/par', corsPreflight) router.post( '/oauth/par', - jsonHandler(async function (req, _res) { + oauthHandler(async function (req, _res) { const payload = await parseHttpRequest(req, ['json', 'urlencoded']) const credentials = await oauthClientCredentialsSchema @@ -1233,11 +1420,9 @@ export class OAuthProvider extends OAuthVerifier { ) }, 201), ) - // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3 // > If the request did not use the POST method, the authorization server // > responds with an HTTP 405 (Method Not Allowed) status code. - router.options('/oauth/par', corsPreflight) router.all('/oauth/par', (req, res) => { res.writeHead(405).end() }) @@ -1245,7 +1430,7 @@ export class OAuthProvider extends OAuthVerifier { router.options('/oauth/token', corsPreflight) router.post( '/oauth/token', - jsonHandler(async function (req, _res) { + oauthHandler(async function (req, _res) { const payload = await parseHttpRequest(req, ['json', 'urlencoded']) const clientMetadata = @@ -1277,7 +1462,7 @@ export class OAuthProvider extends OAuthVerifier { router.options('/oauth/revoke', corsPreflight) router.post( '/oauth/revoke', - jsonHandler(async function (req, res) { + oauthHandler(async function (req, res) { const payload = await parseHttpRequest(req, ['json', 'urlencoded']) const tokenIdentification = await oauthTokenIdentificationSchema @@ -1291,8 +1476,6 @@ export class OAuthProvider extends OAuthVerifier { } }), ) - - router.options('/oauth/revoke', corsPreflight) router.get( '/oauth/revoke', navigationHandler(async function (req, res) { @@ -1317,9 +1500,10 @@ export class OAuthProvider extends OAuthVerifier { }), ) + router.options('/oauth/introspect', corsPreflight) router.post( '/oauth/introspect', - jsonHandler(async function (req, _res) { + oauthHandler(async function (req, _res) { const payload = await parseHttpRequest(req, ['json', 'urlencoded']) const credentials = await oauthClientCredentialsSchema @@ -1346,7 +1530,7 @@ export class OAuthProvider extends OAuthVerifier { const query = Object.fromEntries(this.url.searchParams) const clientCredentials = await oauthClientCredentialsSchema - .parseAsync(query, { path: ['body'] }) + .parseAsync(query, { path: ['query'] }) .catch(throwInvalidRequest) if ('client_secret' in clientCredentials) { @@ -1362,7 +1546,9 @@ export class OAuthProvider extends OAuthVerifier { res, ) - const data = await server + const result: + | AuthorizationResultRedirect + | AuthorizationResultAuthorize = await server .authorize( clientCredentials, authorizationRequest, @@ -1371,173 +1557,79 @@ export class OAuthProvider extends OAuthVerifier { ) .catch((err) => accessDeniedToRedirectCatcher(req, res, err)) - switch (true) { - case 'redirect' in data: { - return sendAuthorizeRedirect(res, data) - } - case 'authorize' in data: { - await setupCsrfToken(req, res, csrfCookie(data.authorize.uri)) - return server.outputManager.sendAuthorizePage(res, data) - } - default: { - // Should never happen - throw new Error('Unexpected authorization result') - } + if ('redirect' in result) { + return sendAuthorizeRedirect(res, result) + } else { + await setupCsrfToken(req, res, csrfCookie(result.authorize.uri)) + return server.outputManager.sendAuthorizePage(res, result, { + preferredLocales: extractLocales(req), + }) } }), ) - const signInPayloadSchema = z.object({ - csrf_token: z.string(), - request_uri: requestUriSchema, - client_id: clientIdSchema, - credentials: signInCredentialsSchema, - }) + router.post( + '/oauth/authorize/verify-handle-availability', + apiHandler( + z.object({ handle: handleSchema }).strict(), + async function (req, res, data) { + return server.accountManager.verifyHandleAvailability(data.handle) + }, + ), + ) + + router.post( + '/oauth/authorize/sign-up', + apiHandler(signUpDataSchema, async function (req, res, data, ctx) { + return server.signUp(ctx, data) + }), + ) - router.options('/oauth/authorize/sign-in', corsPreflight) router.post( '/oauth/authorize/sign-in', - jsonHandler(async function (req, res) { - validateFetchMode(req, res, ['same-origin']) - validateFetchSite(req, res, ['same-origin']) - validateSameOrigin(req, res, issuerOrigin) - - const payload = await parseHttpRequest(req, ['json']) - const input = await signInPayloadSchema.parseAsync(payload, { - path: ['body'], - }) - - validateReferer(req, res, { - origin: issuerOrigin, - pathname: '/oauth/authorize', - }) - validateCsrfToken( - req, - res, - input.csrf_token, - csrfCookie(input.request_uri), - ) - - const { deviceId } = await server.deviceManager.load(req, res, true) - - return server.signIn( - deviceId, - input.request_uri, - input.client_id, - input.credentials, - ) + apiHandler(signInDataSchema, async function (req, res, data, ctx) { + return server.signIn(ctx, data) }), ) - const acceptQuerySchema = z.object({ - csrf_token: z.string(), - request_uri: requestUriSchema, - client_id: clientIdSchema, - account_sub: z.string(), - }) + router.post( + '/oauth/authorize/reset-password-request', + apiHandler( + resetPasswordRequestDataSchema, + async function (req, res, data) { + await server.accountManager.resetPasswordRequest(data) + }, + ), + ) + + router.post( + '/oauth/authorize/reset-password-confirm', + apiHandler( + resetPasswordConfirmDataSchema, + async function (req, res, data) { + await server.accountManager.resetPasswordConfirm(data) + }, + ), + ) - // Though this is a "no-cors" request, meaning that the browser will allow - // any cross-origin request, with credentials, to be sent, the handler will - // 1) validate the request origin, - // 2) validate the CSRF token, - // 3) validate the referer, - // 4) validate the sec-fetch-site header, - // 4) validate the sec-fetch-mode header, - // 5) validate the sec-fetch-dest header (see navigationHandler). - // And will error if any of these checks fail. router.get( '/oauth/authorize/accept', - navigationHandler(async function (req, res) { - validateFetchSite(req, res, ['same-origin']) + authorizeRedirectNavigationHandler(async function (req, res, ctx) { + const sub = this.url.searchParams.get('account_sub') + if (!sub) throw new InvalidRequestError('Account sub not provided') - const query = Object.fromEntries(this.url.searchParams) - const input = await acceptQuerySchema.parseAsync(query, { - path: ['query'], - }) - - validateReferer(req, res, { - origin: issuerOrigin, - pathname: '/oauth/authorize', - searchParams: [ - ['request_uri', input.request_uri], - ['client_id', input.client_id], - ], - }) - validateCsrfToken( - req, - res, - input.csrf_token, - csrfCookie(input.request_uri), - true, - ) - - const { deviceId, deviceMetadata } = await server.deviceManager.load( - req, - res, - ) - - const data = await server - .acceptRequest( - input.request_uri, - input.client_id, - input.account_sub, - deviceId, - deviceMetadata, - ) + return server + .acceptRequest(ctx, sub) .catch((err) => accessDeniedToRedirectCatcher(req, res, err)) - - return await sendAuthorizeRedirect(res, data) }), ) - const rejectQuerySchema = z.object({ - csrf_token: z.string(), - request_uri: requestUriSchema, - client_id: clientIdSchema, - }) - - // Though this is a "no-cors" request, meaning that the browser will allow - // any cross-origin request, with credentials, to be sent, the handler will - // 1) validate the request origin, - // 2) validate the CSRF token, - // 3) validate the referer, - // 4) validate the sec-fetch-site header, - // 4) validate the sec-fetch-mode header, - // 5) validate the sec-fetch-dest header (see navigationHandler). - // And will error if any of these checks fail. router.get( '/oauth/authorize/reject', - navigationHandler(async function (req, res) { - validateFetchSite(req, res, ['same-origin']) - - const query = Object.fromEntries(this.url.searchParams) - const input = await rejectQuerySchema.parseAsync(query, { - path: ['query'], - }) - - validateReferer(req, res, { - origin: issuerOrigin, - pathname: '/oauth/authorize', - searchParams: [ - ['request_uri', input.request_uri], - ['client_id', input.client_id], - ], - }) - validateCsrfToken( - req, - res, - input.csrf_token, - csrfCookie(input.request_uri), - true, - ) - - const { deviceId } = await server.deviceManager.load(req, res) - - const data = await server - .rejectRequest(deviceId, input.request_uri, input.client_id) + authorizeRedirectNavigationHandler(async function (req, res, ctx) { + return server + .rejectRequest(ctx) .catch((err) => accessDeniedToRedirectCatcher(req, res, err)) - - return await sendAuthorizeRedirect(res, data) }), ) diff --git a/packages/oauth/oauth-provider/src/oauth-verifier.ts b/packages/oauth/oauth-provider/src/oauth-verifier.ts index 4c1b60b27..66598f25b 100644 --- a/packages/oauth/oauth-provider/src/oauth-verifier.ts +++ b/packages/oauth/oauth-provider/src/oauth-verifier.ts @@ -94,8 +94,10 @@ export class OAuthVerifier { : new ReplayStoreMemory(), accessTokenType = AccessTokenType.jwt, - ...dpopMgrOptions + ...rest }: OAuthVerifierOptions) { + const dpopMgrOptions: DpopManagerOptions = rest + const issuerParsed = oauthIssuerIdentifierSchema.parse(issuer) const issuerUrl = new URL(issuerParsed) diff --git a/packages/oauth/oauth-provider/src/output/build-authorize-data.ts b/packages/oauth/oauth-provider/src/output/build-authorize-data.ts index d86bf80b5..3ccf0cb09 100644 --- a/packages/oauth/oauth-provider/src/output/build-authorize-data.ts +++ b/packages/oauth/oauth-provider/src/output/build-authorize-data.ts @@ -31,7 +31,7 @@ export type AuthorizationResultAuthorize = { } // TODO: find a way to share this type with the frontend code -// (app/backend-data.ts) +// (app/backend-types.ts) type Session = { account: Account @@ -47,7 +47,6 @@ export type AuthorizeData = { clientMetadata: OAuthClientMetadata clientTrusted: boolean requestUri: string - csrfCookie: string loginHint?: string scopeDetails?: ScopeDetail[] newSessionsRequireConsent: boolean @@ -62,7 +61,6 @@ export function buildAuthorizeData( clientMetadata: data.client.metadata, clientTrusted: data.client.info.isTrusted, requestUri: data.authorize.uri, - csrfCookie: `csrf-${data.authorize.uri}`, loginHint: data.parameters.login_hint, newSessionsRequireConsent: data.parameters.prompt === 'consent', scopeDetails: data.authorize.scopeDetails, diff --git a/packages/oauth/oauth-provider/src/output/build-customization-data.ts b/packages/oauth/oauth-provider/src/output/build-customization-data.ts new file mode 100644 index 000000000..7eaf57f3a --- /dev/null +++ b/packages/oauth/oauth-provider/src/output/build-customization-data.ts @@ -0,0 +1,189 @@ +import { z } from 'zod' +import { hcaptchaConfigSchema } from '../lib/hcaptcha.js' +import { isLinkRel } from '../lib/html/build-document.js' +import { multiLangStringSchema } from '../lib/locale.js' +export { type HcaptchaConfig, hcaptchaConfigSchema } from '../lib/hcaptcha.js' + +// Matches colors defined in tailwind.config.js +export const colorNames = ['brand', 'error', 'warning', 'success'] as const +export const colorNameSchema = z.enum(colorNames) +export type ColorName = z.infer + +export const ColorsDefinitionSchema = z.record(colorNameSchema, z.string()) +export type ColorsDefinition = z.infer + +export const localizedStringSchema = z.union([ + z.string(), + multiLangStringSchema, +]) +export type LocalizedString = z.infer + +export const linkRelSchema = z.string().refine(isLinkRel, 'Invalid link rel') +export type LinkRel = z.infer + +export const linkDefinitionSchema = z.object({ + title: localizedStringSchema, + href: z.string().url(), + rel: linkRelSchema.optional(), +}) +export type LinkDefinition = z.infer + +/** + * Aesthetic customization + */ +export const brandingConfigSchema = z.object({ + name: z.string().optional(), + logo: z.string().optional(), + colors: ColorsDefinitionSchema.optional(), + links: z.array(linkDefinitionSchema).readonly().optional(), +}) +export type BrandingConfig = z.infer + +export const customizationSchema = z.object({ + /** + * Available user domains that can be used to sign up. A non-empty array + * is required to enable the sign-up feature. + */ + availableUserDomains: z.array(z.string()).optional(), + /** + * UI customizations + */ + branding: brandingConfigSchema.optional(), + /** + * Is an invite code required to sign up? + */ + inviteCodeRequired: z.boolean().optional(), + /** + * Enables hCaptcha during sign-up. + */ + hcaptcha: hcaptchaConfigSchema.optional(), +}) +export type Customization = z.infer + +export type CustomizationData = { + // Functional customization + hcaptchaSiteKey?: string + inviteCodeRequired?: boolean + availableUserDomains?: string[] + + // Aesthetic customization + name?: string + logo?: string + links?: readonly LinkDefinition[] +} + +export function buildCustomizationData({ + branding, + availableUserDomains, + inviteCodeRequired, + hcaptcha, +}: Customization): CustomizationData { + // @NOTE the front end does not need colors here as they will be injected as + // CSS variables. + // @NOTE We only copy the values explicitly needed to avoid leaking sensitive + // data (in case the caller passed more than what we expect). + return { + availableUserDomains, + inviteCodeRequired, + hcaptchaSiteKey: hcaptcha?.siteKey, + name: branding?.name, + logo: branding?.logo, + links: branding?.links, + } +} + +export function buildCustomizationCss({ branding }: Customization) { + const vars = Array.from(buildCustomizationVars(branding)) + if (vars.length) return `:root { ${vars.join(' ')} }` + + return '' +} + +function* buildCustomizationVars(branding?: BrandingConfig) { + if (branding?.colors) { + for (const name of colorNames) { + const value = branding.colors[name] + if (!value) continue + + // Skip undefined values + if (value === undefined) continue + + const { r, g, b, a } = parseColor(value) + + // Tailwind does not apply alpha values to base colors + if (a !== undefined) throw new TypeError('Alpha not supported') + + const contrast = computeLuma({ r, g, b }) > 128 ? '0 0 0' : '255 255 255' + + yield `--color-${name}: ${r} ${g} ${b};` + yield `--color-${name}-c: ${contrast};` + } + } +} + +type RgbaColor = { r: number; g: number; b: number; a?: number } +function parseColor(color: unknown): RgbaColor { + if (typeof color !== 'string') { + throw new TypeError(`Invalid color value: ${typeof color}`) + } + + if (color.startsWith('#')) { + if (color.length === 4 || color.length === 5) { + const r = parseUi8Hex(color.slice(1, 2)) + const g = parseUi8Hex(color.slice(2, 3)) + const b = parseUi8Hex(color.slice(3, 4)) + const a = color.length > 4 ? parseUi8Hex(color.slice(4, 5)) : undefined + return { r, g, b, a } + } + + if (color.length === 7 || color.length === 9) { + const r = parseUi8Hex(color.slice(1, 3)) + const g = parseUi8Hex(color.slice(3, 5)) + const b = parseUi8Hex(color.slice(5, 7)) + const a = color.length > 8 ? parseUi8Hex(color.slice(7, 9)) : undefined + return { r, g, b, a } + } + + throw new TypeError(`Invalid hex color: ${color}`) + } + + const rgbMatch = color.match( + /^\s*rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/, + ) + if (rgbMatch) { + const r = parseUi8Dec(rgbMatch[1]) + const g = parseUi8Dec(rgbMatch[2]) + const b = parseUi8Dec(rgbMatch[3]) + return { r, g, b } + } + + const rgbaMatch = color.match( + /^\s*rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/, + ) + if (rgbaMatch) { + const r = parseUi8Dec(rgbaMatch[1]) + const g = parseUi8Dec(rgbaMatch[2]) + const b = parseUi8Dec(rgbaMatch[3]) + const a = parseUi8Dec(rgbaMatch[4]) + return { r, g, b, a } + } + + throw new TypeError(`Unsupported color format: ${color}`) +} + +function computeLuma({ r, g, b }: RgbaColor) { + return 0.299 * r + 0.587 * g + 0.114 * b +} + +function parseUi8Hex(v: string) { + return asUi8(parseInt(v, 16)) +} + +function parseUi8Dec(v: string) { + return asUi8(parseInt(v, 10)) +} + +function asUi8(v: number) { + if (v >= 0 && v <= 255 && v === (v | 0)) return v + throw new TypeError(`Invalid color component: ${v}`) +} diff --git a/packages/oauth/oauth-provider/src/output/customization.ts b/packages/oauth/oauth-provider/src/output/customization.ts deleted file mode 100644 index 855e01992..000000000 --- a/packages/oauth/oauth-provider/src/output/customization.ts +++ /dev/null @@ -1,118 +0,0 @@ -// Matches colors defined in tailwind.config.js -const colorNames = ['brand', 'error', 'warning'] as const -type ColorName = (typeof colorNames)[number] -const isColorName = (name: string): name is ColorName => - (colorNames as readonly string[]).includes(name) - -export type Customization = { - name?: string - logo?: string - colors?: { [_ in ColorName]?: string } - links?: Array<{ - href: string - title: string - rel?: string - }> -} - -export function buildCustomizationData({ - name, - logo, - links, -}: Customization = {}) { - return { - name, - logo, - links, - } -} - -export function buildCustomizationCss(customization?: Customization) { - const vars = Array.from(buildCustomizationVars(customization)) - if (vars.length) return `:root { ${vars.join(' ')} }` - - return '' -} - -export function* buildCustomizationVars(customization?: Customization) { - if (customization?.colors) { - for (const [name, value] of Object.entries(customization.colors)) { - if (!isColorName(name)) { - throw new TypeError(`Invalid color name: ${name}`) - } - - // Skip undefined values - if (value === undefined) continue - - const { r, g, b, a } = parseColor(value) - - // Tailwind does not apply alpha values to base colors - if (a !== undefined) throw new TypeError('Alpha not supported') - - yield `--color-${name}: ${r} ${g} ${b};` - } - } -} - -type RgbaColor = { r: number; g: number; b: number; a?: number } -function parseColor(color: unknown): RgbaColor { - if (typeof color !== 'string') { - throw new TypeError(`Invalid color value: ${typeof color}`) - } - - if (color.startsWith('#')) { - if (color.length === 4 || color.length === 5) { - const r = parseUi8Hex(color.slice(1, 2)) - const g = parseUi8Hex(color.slice(2, 3)) - const b = parseUi8Hex(color.slice(3, 4)) - const a = color.length > 4 ? parseUi8Hex(color.slice(4, 5)) : undefined - return { r, g, b, a } - } - - if (color.length === 7 || color.length === 9) { - const r = parseUi8Hex(color.slice(1, 3)) - const g = parseUi8Hex(color.slice(3, 5)) - const b = parseUi8Hex(color.slice(5, 7)) - const a = color.length > 8 ? parseUi8Hex(color.slice(7, 9)) : undefined - return { r, g, b, a } - } - - throw new TypeError(`Invalid hex color: ${color}`) - } - - const rgbMatch = color.match( - /^\s*rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/, - ) - if (rgbMatch) { - const r = parseUi8Dec(rgbMatch[1]) - const g = parseUi8Dec(rgbMatch[2]) - const b = parseUi8Dec(rgbMatch[3]) - return { r, g, b } - } - - const rgbaMatch = color.match( - /^\s*rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/, - ) - if (rgbaMatch) { - const r = parseUi8Dec(rgbaMatch[1]) - const g = parseUi8Dec(rgbaMatch[2]) - const b = parseUi8Dec(rgbaMatch[3]) - const a = parseUi8Dec(rgbaMatch[4]) - return { r, g, b, a } - } - - throw new TypeError(`Unsupported color format: ${color}`) -} - -function parseUi8Hex(v: string) { - return asUi8(parseInt(v, 16)) -} - -function parseUi8Dec(v: string) { - return asUi8(parseInt(v, 10)) -} - -function asUi8(v: number) { - if (v >= 0 && v <= 255 && v === (v | 0)) return v - throw new TypeError(`Invalid color component: ${v}`) -} diff --git a/packages/oauth/oauth-provider/src/output/output-manager.ts b/packages/oauth/oauth-provider/src/output/output-manager.ts index c8a35d4f7..645b459b5 100644 --- a/packages/oauth/oauth-provider/src/output/output-manager.ts +++ b/packages/oauth/oauth-provider/src/output/output-manager.ts @@ -1,86 +1,149 @@ import type { ServerResponse } from 'node:http' import { Asset } from '../assets/asset.js' -import { getAsset } from '../assets/index.js' -import { Html, cssCode, html } from '../lib/html/index.js' +import { enumerateAssets } from '../assets/index.js' +import { CspConfig, mergeCsp } from '../lib/csp/index.js' +import { + Html, + LinkAttrs, + MetaAttrs, + cssCode, + html, + isLinkRel, +} from '../lib/html/index.js' +import { AVAILABLE_LOCALES, Locale, isAvailableLocale } from '../lib/locale.js' import { AuthorizationResultAuthorize, buildAuthorizeData, } from './build-authorize-data.js' -import { buildErrorPayload, buildErrorStatus } from './build-error-payload.js' import { Customization, + LinkDefinition, buildCustomizationCss, buildCustomizationData, -} from './customization.js' -import { declareBackendData, sendWebPage } from './send-web-page.js' +} from './build-customization-data.js' +import { buildErrorPayload, buildErrorStatus } from './build-error-payload.js' +import { assetToCsp, declareBackendData, sendWebPage } from './send-web-page.js' + +const HCAPTCHA_CSP = { + 'script-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'], + 'frame-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'], + 'style-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'], + 'connect-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'], +} as const satisfies CspConfig + +export type SendPageOptions = { + preferredLocales?: readonly string[] +} export class OutputManager { - readonly customizationScript: Html - readonly customizationStyle: Html - readonly customizationLinks?: Customization['links'] + readonly links?: readonly LinkDefinition[] + readonly meta: readonly MetaAttrs[] = [ + { name: 'robots', content: 'noindex' }, + { name: 'description', content: 'ATProto OAuth authorization page' }, + ] + readonly scripts: readonly (Asset | Html)[] + readonly styles: readonly (Asset | Html)[] + readonly csp: CspConfig - // Could technically cause an "UnhandledPromiseRejection", which might cause - // the process to exit. This is intentional, as it's a critical error. It - // should never happen in practice, as the built assets are bundled with the - // package. - readonly assetsPromise: Promise<[js: Asset, css: Asset]> = Promise.all([ - getAsset('main.js'), - getAsset('main.css'), - ] as const) + constructor(customization: Customization) { + this.links = customization.branding?.links - constructor(customization?: Customization) { - // Note: building this here for two reasons: + const scripts = Array.from(enumerateAssets('application/javascript')) + const styles = Array.from(enumerateAssets('text/css')) + + // Note: building scripts/styles/csp here for two reasons: // 1. To avoid re-building it on every request - // 2. To throw during init if the customization is invalid - this.customizationScript = declareBackendData( - '__customizationData', - buildCustomizationData(customization), - ) - this.customizationStyle = cssCode(buildCustomizationCss(customization)) - this.customizationLinks = customization?.links + // 2. To throw during init if the customization/config is invalid + + this.scripts = [ + declareBackendData('__availableLocales', AVAILABLE_LOCALES), + declareBackendData( + '__customizationData', + buildCustomizationData(customization), + ), + // Last (to be able to read the "backend data" variables) + ...scripts.filter((asset) => asset.isEntry), + ] + + this.styles = [ + // First (to be overridden by customization) + ...styles, + cssCode(buildCustomizationCss(customization)), + ] + + const customizationCsp = customization?.hcaptcha ? HCAPTCHA_CSP : undefined + const assetsCsp: CspConfig = { + 'script-src': scripts.map(assetToCsp), + 'style-src': styles.map(assetToCsp), + } + + this.csp = mergeCsp(customizationCsp, assetsCsp) } async sendAuthorizePage( res: ServerResponse, data: AuthorizationResultAuthorize, + options?: SendPageOptions, ): Promise { - const [jsAsset, cssAsset] = await this.assetsPromise + const locale = negotiateLocale( + data.parameters.ui_locales?.split(' ') ?? options?.preferredLocales, + ) return sendWebPage(res, { scripts: [ declareBackendData('__authorizeData', buildAuthorizeData(data)), - this.customizationScript, - jsAsset, // Last (to be able to read the "backend data" variables) + ...this.scripts, ], - styles: [ - cssAsset, // First (to be overridden by customization) - this.customizationStyle, - ], - links: this.customizationLinks, - htmlAttrs: { lang: 'en' }, - title: 'Authorize', + styles: this.styles, + meta: this.meta, + links: this.buildLinks(locale), + htmlAttrs: { lang: locale }, body: html`

`, + csp: this.csp, }) } - async sendErrorPage(res: ServerResponse, err: unknown): Promise { - const [jsAsset, cssAsset] = await this.assetsPromise + async sendErrorPage( + res: ServerResponse, + err: unknown, + options?: SendPageOptions, + ): Promise { + const locale = negotiateLocale(options?.preferredLocales) return sendWebPage(res, { status: buildErrorStatus(err), scripts: [ declareBackendData('__errorData', buildErrorPayload(err)), - this.customizationScript, - jsAsset, // Last (to be able to read the "backend data" variables) + ...this.scripts, ], - styles: [ - cssAsset, // First (to be overridden by customization) - this.customizationStyle, - ], - links: this.customizationLinks, - htmlAttrs: { lang: 'en' }, - title: 'Error', + styles: this.styles, + meta: this.meta, + links: this.buildLinks(locale), + htmlAttrs: { lang: locale }, body: html`
`, + csp: this.csp, }) } + + buildLinks(locale: Locale) { + return this.links + ?.map(({ rel, href, title }: LinkDefinition): LinkAttrs | undefined => + isLinkRel(rel) + ? typeof title === 'string' + ? { href, rel, title } + : { href, rel, title: title[locale] || title.en } + : undefined, + ) + .filter((v) => v != null) + } +} + +function negotiateLocale(desiredLocales?: readonly string[]): Locale { + if (desiredLocales) { + for (const locale of desiredLocales) { + if (locale === '*') break // use default + if (isAvailableLocale(locale)) return locale + } + } + return 'en' } diff --git a/packages/oauth/oauth-provider/src/output/send-authorize-redirect.ts b/packages/oauth/oauth-provider/src/output/send-authorize-redirect.ts index 7d5aa46a7..c15a935f9 100644 --- a/packages/oauth/oauth-provider/src/output/send-authorize-redirect.ts +++ b/packages/oauth/oauth-provider/src/output/send-authorize-redirect.ts @@ -11,31 +11,42 @@ import { sendWebPage } from './send-web-page.js' // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-7.5.4 const REDIRECT_STATUS_CODE = 303 -export type AuthorizationResponseParameters = { - // Will be added from AuthorizationResultRedirect['issuer'] - // iss: string // rfc9207 +/** + * @note `iss` and `state` will be added from the + * {@link AuthorizationResultRedirect} object. + */ +export type AuthorizationRedirectParameters = + | { + // iss: string + // state?: string + code: Code + id_token?: string + access_token?: string + token_type?: OAuthTokenType + expires_in?: string + } + | { + // iss: string + // state?: string + error: string + error_description?: string + error_uri?: string + } - // Will be added from AuthorizationResultRedirect['parameters'] - // state?: string +const SUCCESS_REDIRECT_KEYS = [ + 'code', + 'id_token', + 'access_token', + 'expires_in', + 'token_type', +] as const - code?: Code - id_token?: string - access_token?: string - token_type?: OAuthTokenType - expires_in?: string - - response?: string // FAPI JARM - session_state?: string // OIDC Session Management - - error?: string - error_description?: string - error_uri?: string -} +const ERROR_REDIRECT_KEYS = ['error', 'error_description', 'error_uri'] as const export type AuthorizationResultRedirect = { issuer: string parameters: OAuthAuthorizationRequestParameters - redirect: AuthorizationResponseParameters + redirect: AuthorizationRedirectParameters } export async function sendAuthorizeRedirect( @@ -49,23 +60,19 @@ export async function sendAuthorizeRedirect( const mode = parameters.response_mode || 'query' // @TODO: default should depend on response_type - const entries: [string, string][] = Object.entries({ - iss: issuer, // rfc9207 - state: parameters.state, + const entries: [string, string][] = [ + ['iss', issuer], // rfc9207 + ] - response: redirect.response, // FAPI JARM - session_state: redirect.session_state, // OIDC Session Management + if (parameters.state != null) { + entries.push(['state', parameters.state]) + } - code: redirect.code, - id_token: redirect.id_token, - access_token: redirect.access_token, - expires_in: redirect.expires_in, - token_type: redirect.token_type, - - error: redirect.error, - error_description: redirect.error_description, - error_uri: redirect.error_uri, - }).filter((entry): entry is [string, string] => entry[1] != null) + const keys = 'code' in redirect ? SUCCESS_REDIRECT_KEYS : ERROR_REDIRECT_KEYS + for (const key of keys) { + const value = redirect[key] + if (value != null) entries.push([key, value]) + } res.setHeader('Cache-Control', 'no-store') diff --git a/packages/oauth/oauth-provider/src/output/send-web-page.ts b/packages/oauth/oauth-provider/src/output/send-web-page.ts index 32e6ba77c..5953e2c24 100644 --- a/packages/oauth/oauth-provider/src/output/send-web-page.ts +++ b/packages/oauth/oauth-provider/src/output/send-web-page.ts @@ -1,5 +1,6 @@ import { createHash } from 'node:crypto' import type { ServerResponse } from 'node:http' +import { CspConfig, CspValue, buildCsp, mergeCsp } from '../lib/csp/index.js' import { AssetRef, BuildDocumentOptions, @@ -13,16 +14,38 @@ export function declareBackendData(name: string, data: unknown) { // The script tag is removed after the data is assigned to the global variable // to prevent other scripts from deducing the value of the variable. The "app" // script will read the global variable and then unset it. See - // "readBackendData" in "src/assets/app/backend-data.ts". + // "readBackendData" in "src/assets/app/backend-types.ts". return js`window[${name}]=${data};document.currentScript.remove();` } -export type SendWebPageOptions = BuildDocumentOptions & WriteResponseOptions +export type SendWebPageOptions = BuildDocumentOptions & + WriteResponseOptions & { + csp?: CspConfig + } export async function sendWebPage( res: ServerResponse, options: SendWebPageOptions, ): Promise { + const csp = mergeCsp(options.csp, { + 'default-src': ["'none'"], + 'base-uri': options.base?.origin as undefined | `https://${string}`, + 'script-src': ["'self'", ...assetsToCsp(options.scripts)], + 'style-src': ["'self'", ...assetsToCsp(options.styles)], + 'img-src': ["'self'", 'data:', 'https:'], + 'connect-src': ["'self'"], + 'upgrade-insecure-requests': true, + + // Prevents the CSP to be embedded in a page : + 'frame-ancestors': ["'none'"], + }) + + // @NOTE the csp string might become too long. However, since we need to + // specify the "frame-ancestors" directive, we can't use a meta tag. For that + // reason, we won't try to avoid too long headers and let the proxy throw + // in case of a too long header. + res.setHeader('Content-Security-Policy', buildCsp(csp)) + // @TODO: make these headers configurable (?) res.setHeader('Permissions-Policy', 'otp-credentials=*, document-domain=()') res.setHeader('Cross-Origin-Embedder-Policy', 'credentialless') @@ -33,36 +56,27 @@ export async function sendWebPage( res.setHeader('X-Content-Type-Options', 'nosniff') res.setHeader('X-XSS-Protection', '0') res.setHeader('Strict-Transport-Security', 'max-age=63072000') - res.setHeader( - 'Content-Security-Policy', - [ - `default-src 'none'`, - `frame-ancestors 'none'`, - `form-action 'none'`, - `base-uri ${options.base?.origin || `'none'`}`, - `script-src 'self' ${ - options.scripts?.map(assetToHash).map(hashToCspRule).join(' ') ?? '' - }`, - `style-src 'self' ${ - options.styles?.map(assetToHash).map(hashToCspRule).join(' ') ?? '' - }`, - `img-src 'self' data: https:`, - `connect-src 'self'`, - `upgrade-insecure-requests`, - ].join('; '), - ) const html = buildDocument(options) return writeHtml(res, html.toString(), options) } -function assetToHash(asset: Html | AssetRef): string { - return asset instanceof Html - ? createHash('sha256').update(asset.toString()).digest('base64') - : asset.sha256 +export function* assetsToCsp( + assets?: Iterable, +): Generator { + if (assets) { + for (const asset of assets) { + yield assetToCsp(asset) + } + } } -function hashToCspRule(hash: string): string { - return `'sha256-${hash}'` +export function assetToCsp(asset: Html | AssetRef): CspValue { + if (asset instanceof Html) { + const hash = createHash('sha256').update(asset.toString()).digest('base64') + return `'sha256-${hash}'` + } else { + return `'sha256-${asset.sha256}'` + } } diff --git a/packages/oauth/oauth-provider/src/request/request-manager.ts b/packages/oauth/oauth-provider/src/request/request-manager.ts index 9fa872a81..d551e9c7c 100644 --- a/packages/oauth/oauth-provider/src/request/request-manager.ts +++ b/packages/oauth/oauth-provider/src/request/request-manager.ts @@ -308,13 +308,13 @@ export class RequestManager { async get( uri: RequestUri, - clientId: ClientId, deviceId: DeviceId, + clientId?: ClientId, ): Promise { const id = decodeRequestUri(uri) const data = await this.store.readRequest(id) - if (!data) throw new InvalidRequestError(`Unknown request_uri "${uri}"`) + if (!data) throw new InvalidRequestError('Unknown request_uri') const updates: UpdateRequestData = {} @@ -336,7 +336,7 @@ export class RequestManager { ) } - if (data.clientId !== clientId) { + if (clientId != null && data.clientId !== clientId) { throw new AccessDeniedError( data.parameters, 'This request was initiated for another client', @@ -380,7 +380,7 @@ export class RequestManager { const id = decodeRequestUri(uri) const data = await this.store.readRequest(id) - if (!data) throw new InvalidRequestError(`Unknown request_uri "${uri}"`) + if (!data) throw new InvalidRequestError('Unknown request_uri') try { if (data.expiresAt < new Date()) { diff --git a/packages/oauth/oauth-provider/src/request/request-store.ts b/packages/oauth/oauth-provider/src/request/request-store.ts index 0763e1e81..02faca873 100644 --- a/packages/oauth/oauth-provider/src/request/request-store.ts +++ b/packages/oauth/oauth-provider/src/request/request-store.ts @@ -1,12 +1,12 @@ -import { Awaitable } from '../lib/util/type.js' +import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js' import { Code } from './code.js' import { RequestData } from './request-data.js' import { RequestId } from './request-id.js' // Export all types needed to implement the RequestStore interface export * from './code.js' -export * from './request-id.js' export * from './request-data.js' +export * from './request-id.js' export type { Awaitable } export type UpdateRequestData = Pick< @@ -31,21 +31,17 @@ export interface RequestStore { findRequestByCode(code: Code): Awaitable } -export function isRequestStore( - implementation: Record & Partial, -): implementation is Record & RequestStore { - return ( - typeof implementation.createRequest === 'function' && - typeof implementation.readRequest === 'function' && - typeof implementation.updateRequest === 'function' && - typeof implementation.deleteRequest === 'function' && - typeof implementation.findRequestByCode === 'function' - ) -} +export const isRequestStore = buildInterfaceChecker([ + 'createRequest', + 'readRequest', + 'updateRequest', + 'deleteRequest', + 'findRequestByCode', +]) -export function ifRequestStore( - implementation?: Record & Partial, -): RequestStore | undefined { +export function ifRequestStore>( + implementation?: V, +): (V & RequestStore) | undefined { if (implementation && isRequestStore(implementation)) { return implementation } diff --git a/packages/oauth/oauth-provider/src/token/token-store.ts b/packages/oauth/oauth-provider/src/token/token-store.ts index 53d8e9a9b..4b985f820 100644 --- a/packages/oauth/oauth-provider/src/token/token-store.ts +++ b/packages/oauth/oauth-provider/src/token/token-store.ts @@ -1,15 +1,15 @@ import { DeviceAccountInfo } from '../account/account-store.js' import { Account } from '../account/account.js' -import { Awaitable } from '../lib/util/type.js' +import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js' import { Code } from '../request/code.js' import { RefreshToken } from './refresh-token.js' import { TokenData } from './token-data.js' import { TokenId } from './token-id.js' // Export all types needed to implement the TokenStore interface -export * from './token-id.js' -export * from './token-data.js' export * from './refresh-token.js' +export * from './token-data.js' +export * from './token-id.js' export type { Awaitable } export type TokenInfo = { @@ -55,22 +55,18 @@ export interface TokenStore { findTokenByCode(code: Code): Awaitable } -export function isTokenStore( - implementation: Record & Partial, -): implementation is Record & TokenStore { - return ( - typeof implementation.createToken === 'function' && - typeof implementation.readToken === 'function' && - typeof implementation.rotateToken === 'function' && - typeof implementation.deleteToken === 'function' && - typeof implementation.findTokenByCode === 'function' && - typeof implementation.findTokenByRefreshToken === 'function' - ) -} +export const isTokenStore = buildInterfaceChecker([ + 'createToken', + 'readToken', + 'deleteToken', + 'rotateToken', + 'findTokenByRefreshToken', + 'findTokenByCode', +]) -export function asTokenStore( - implementation?: Record & Partial, -): TokenStore { +export function asTokenStore>( + implementation?: V, +): V & TokenStore { if (!implementation || !isTokenStore(implementation)) { throw new Error('Invalid TokenStore implementation') } diff --git a/packages/oauth/oauth-provider/tailwind.config.js b/packages/oauth/oauth-provider/tailwind.config.js index 57dc9f6e1..55745b36f 100644 --- a/packages/oauth/oauth-provider/tailwind.config.js +++ b/packages/oauth/oauth-provider/tailwind.config.js @@ -17,8 +17,13 @@ export default { extend: { colors: { brand: 'rgb(var(--color-brand) / )', + 'brand-c': 'rgb(var(--color-brand-c) / )', error: 'rgb(var(--color-error) / )', + 'error-c': 'rgb(var(--color-error-c) / )', warning: 'rgb(var(--color-warning) / )', + 'warning-c': 'rgb(var(--color-warning-c) / )', + success: 'rgb(var(--color-success) / )', + 'success-c': 'rgb(var(--color-success-c) / )', }, }, }, diff --git a/packages/oauth/oauth-provider/vite.config.mjs b/packages/oauth/oauth-provider/vite.config.mjs new file mode 100644 index 000000000..844b02e30 --- /dev/null +++ b/packages/oauth/oauth-provider/vite.config.mjs @@ -0,0 +1,16 @@ +import { lingui } from '@lingui/vite-plugin' +import react from '@vitejs/plugin-react-swc' + +/** + * @see {@link https://vitejs.dev/config/} + * @type {import('vite').UserConfig} + */ +export default { + root: './src/assets/app', + plugins: [ + react({ + plugins: [['@lingui/swc-plugin', {}]], + }), + lingui(), + ], +} diff --git a/packages/oauth/oauth-types/src/oauth-authorization-request-parameters.ts b/packages/oauth/oauth-types/src/oauth-authorization-request-parameters.ts index 13e4bf71f..4cee85c43 100644 --- a/packages/oauth/oauth-types/src/oauth-authorization-request-parameters.ts +++ b/packages/oauth/oauth-types/src/oauth-authorization-request-parameters.ts @@ -66,7 +66,7 @@ export const oauthAuthorizationRequestParametersSchema = z.object({ ui_locales: z .string() - .regex(/^[a-z]{2}(-[A-Z]{2})?( [a-z]{2}(-[A-Z]{2})?)*$/) // fr-CA fr en + .regex(/^[a-z]{2,3}(-[A-Z]{2})?( [a-z]{2,3}(-[A-Z]{2})?)*$/) // fr-CA fr en .optional(), // Previous ID Token, should be provided when prompt=none is used diff --git a/packages/pds/src/account-manager/index.ts b/packages/pds/src/account-manager/account-manager.ts similarity index 59% rename from packages/pds/src/account-manager/index.ts rename to packages/pds/src/account-manager/account-manager.ts index 12d744c40..1eb21b4f1 100644 --- a/packages/pds/src/account-manager/index.ts +++ b/packages/pds/src/account-manager/account-manager.ts @@ -1,67 +1,43 @@ import { KeyObject } from 'node:crypto' -import { Selectable } from 'kysely' import { CID } from 'multiformats/cid' import { HOUR, wait } from '@atproto/common' -import { - Account, - AccountInfo, - AccountStore, - Code, - DeviceData, - DeviceId, - DeviceStore, - FoundRequestResult, - NewTokenData, - RefreshToken, - RequestData, - RequestId, - RequestStore, - SignInCredentials, - TokenData, - TokenId, - TokenInfo, - TokenStore, - UpdateRequestData, -} from '@atproto/oauth-provider' -import { AuthRequiredError } from '@atproto/xrpc-server' -import { ActorStore } from '../actor-store/actor-store' +import { IdResolver } from '@atproto/identity' +import { isValidTld } from '@atproto/syntax' +import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' import { AuthScope } from '../auth-verifier' -import { BackgroundQueue } from '../background' +import { DatabaseConfig } from '../config' import { softDeleted } from '../db' -import { ImageUrlBuilder } from '../image/image-url-builder' +import { hasExplicitSlur } from '../handle/explicit-slurs' +import { + baseNormalizeAndValidate, + ensureHandleServiceConstraints, + isServiceDomain, +} from '../handle/index' import { StatusAttr } from '../lexicon/types/com/atproto/admin/defs' import { AccountDb, EmailTokenPurpose, getDb, getMigrator } from './db' import * as account from './helpers/account' import { AccountStatus, ActorAccount } from './helpers/account' import * as auth from './helpers/auth' -import * as authRequest from './helpers/authorization-request' -import * as device from './helpers/device' -import * as deviceAccount from './helpers/device-account' import * as emailToken from './helpers/email-token' import * as invite from './helpers/invite' import * as password from './helpers/password' import * as repo from './helpers/repo' import * as scrypt from './helpers/scrypt' import * as token from './helpers/token' -import * as usedRefreshToken from './helpers/used-refresh-token' export { AccountStatus, formatAccountStatus } from './helpers/account' -export class AccountManager - implements AccountStore, RequestStore, DeviceStore, TokenStore -{ - db: AccountDb +export class AccountManager { + readonly db: AccountDb constructor( - private actorStore: ActorStore, - private imageUrlBuilder: ImageUrlBuilder, - private backgroundQueue: BackgroundQueue, - dbLocation: string, - private jwtKey: KeyObject, - private serviceDid: string, - disableWalAutoCheckpoint = false, + readonly idResolver: IdResolver, + readonly jwtKey: KeyObject, + readonly serviceDid: string, + readonly serviceHandleDomains: string[], + db: DatabaseConfig, ) { - this.db = getDb(dbLocation, disableWalAutoCheckpoint) + this.db = getDb(db.accountDbLoc, db.disableWalAutoCheckpoint) } async migrateOrThrow() { @@ -121,7 +97,67 @@ export class AccountManager return res.active ? AccountStatus.Active : res.status } - async createAccount(opts: { + async normalizeAndValidateHandle( + handle: string, + { + did, + allowAnyValid, + }: { + did?: string + allowAnyValid?: boolean + } = {}, + ): Promise { + const normalized = baseNormalizeAndValidate(handle) + + // tld validation + if (!isValidTld(normalized)) { + throw new InvalidRequestError( + 'Handle TLD is invalid or disallowed', + 'InvalidHandle', + ) + } + // slur check + if (!allowAnyValid && hasExplicitSlur(normalized)) { + throw new InvalidRequestError( + 'Inappropriate language in handle', + 'InvalidHandle', + ) + } + if (isServiceDomain(normalized, this.serviceHandleDomains)) { + // verify constraints on a service domain + ensureHandleServiceConstraints( + normalized, + this.serviceHandleDomains, + allowAnyValid, + ) + } else { + if (did == null) { + throw new InvalidRequestError( + 'Not a supported handle domain', + 'UnsupportedDomain', + ) + } + // verify resolution of a non-service domain + const resolvedDid = await this.idResolver.handle.resolve(normalized) + if (resolvedDid !== did) { + throw new InvalidRequestError('External handle did not resolve to DID') + } + } + + return normalized + } + + async createAccount({ + did, + handle, + email, + password, + repoCid, + repoRev, + inviteCode, + deactivated, + refreshJwt, + }: { did: string handle: string email?: string @@ -130,28 +166,12 @@ export class AccountManager repoRev: string inviteCode?: string deactivated?: boolean + refreshJwt?: string }) { - const { - did, - handle, - email, - password, - repoCid, - repoRev, - inviteCode, - deactivated, - } = opts const passwordScrypt = password ? await scrypt.genSaltAndHash(password) : undefined - const { accessJwt, refreshJwt } = await auth.createTokens({ - did, - jwtKey: this.jwtKey, - serviceDid: this.serviceDid, - scope: AuthScope.Access, - }) - const refreshPayload = auth.decodeRefreshToken(refreshJwt) const now = new Date().toISOString() await this.db.transaction(async (dbTxn) => { if (inviteCode) { @@ -167,10 +187,36 @@ export class AccountManager inviteCode, now, }), - auth.storeRefreshToken(dbTxn, refreshPayload, null), + refreshJwt && + auth.storeRefreshToken( + dbTxn, + auth.decodeRefreshToken(refreshJwt), + null, + ), repo.updateRoot(dbTxn, did, repoCid, repoRev), ]) }) + } + + async createAccountAndSession(opts: { + did: string + handle: string + email?: string + password?: string + repoCid: CID + repoRev: string + inviteCode?: string + deactivated?: boolean + }) { + const { accessJwt, refreshJwt } = await auth.createTokens({ + did: opts.did, + jwtKey: this.jwtKey, + serviceDid: this.serviceDid, + scope: AuthScope.Access, + }) + + await this.createAccount({ ...opts, refreshJwt }) + return { accessJwt, refreshJwt } } @@ -502,250 +548,4 @@ export class AccountManager ]), ) } - - // AccountStore - - private async buildAccount(row: Selectable): Promise { - const account = deviceAccount.toAccount(row, this.serviceDid) - - if (!account.name || !account.picture) { - const did = account.sub - - const profile = await this.actorStore.read(did, async (store) => { - return store.record.getProfileRecord() - }) - - if (profile) { - const { avatar, displayName } = profile - - account.name ||= displayName - account.picture ||= avatar - ? this.imageUrlBuilder.build('avatar', did, avatar.ref.toString()) - : undefined - } - } - - return account - } - - async authenticateAccount( - { username: identifier, password, remember = false }: SignInCredentials, - deviceId: DeviceId, - ): Promise { - try { - const { user, appPassword } = await this.login({ identifier, password }) - - if (appPassword) { - throw new AuthRequiredError('App passwords are not allowed') - } - - await this.db.executeWithRetry( - deviceAccount.createOrUpdateQB(this.db, deviceId, user.did, remember), - ) - - return await this.getDeviceAccount(deviceId, user.did) - } catch (err) { - if (err instanceof AuthRequiredError) return null - throw err - } - } - - async addAuthorizedClient( - deviceId: DeviceId, - sub: string, - clientId: string, - ): Promise { - await this.db.transaction(async (dbTxn) => { - const row = await deviceAccount - .readQB(dbTxn, deviceId, sub) - .executeTakeFirstOrThrow() - - const { authorizedClients } = deviceAccount.toDeviceAccountInfo(row) - if (!authorizedClients.includes(clientId)) { - await deviceAccount - .updateQB(dbTxn, deviceId, sub, { - authorizedClients: [...authorizedClients, clientId], - }) - .execute() - } - }) - } - - async getDeviceAccount( - deviceId: DeviceId, - sub: string, - ): Promise { - const row = await deviceAccount - .getAccountInfoQB(this.db, deviceId, sub) - .executeTakeFirst() - - if (!row) return null - - return { - account: await this.buildAccount(row), - info: deviceAccount.toDeviceAccountInfo(row), - } - } - - async listDeviceAccounts(deviceId: DeviceId): Promise { - const rows = await deviceAccount - .listRememberedQB(this.db, deviceId) - .execute() - - return Promise.all( - rows.map(async (row) => ({ - account: await this.buildAccount(row), - info: deviceAccount.toDeviceAccountInfo(row), - })), - ) - } - - async removeDeviceAccount(deviceId: DeviceId, sub: string): Promise { - await this.db.executeWithRetry( - deviceAccount.removeQB(this.db, deviceId, sub), - ) - } - - // RequestStore - - async createRequest(id: RequestId, data: RequestData): Promise { - await this.db.executeWithRetry(authRequest.createQB(this.db, id, data)) - } - - async readRequest(id: RequestId): Promise { - try { - const row = await authRequest.readQB(this.db, id).executeTakeFirst() - if (!row) return null - return authRequest.rowToRequestData(row) - } finally { - // Take the opportunity to clean up expired requests. Do this after we got - // the current (potentially expired) request data to allow the provider to - // handle expired requests. - this.backgroundQueue.add(async () => { - await this.db.executeWithRetry(authRequest.removeOldExpiredQB(this.db)) - }) - } - } - - async updateRequest(id: RequestId, data: UpdateRequestData): Promise { - await this.db.executeWithRetry(authRequest.updateQB(this.db, id, data)) - } - - async deleteRequest(id: RequestId): Promise { - await this.db.executeWithRetry(authRequest.removeByIdQB(this.db, id)) - } - - async findRequestByCode(code: Code): Promise { - const row = await authRequest.findByCodeQB(this.db, code).executeTakeFirst() - return row ? authRequest.rowToFoundRequestResult(row) : null - } - - // DeviceStore - - async createDevice(deviceId: DeviceId, data: DeviceData): Promise { - await this.db.executeWithRetry(device.createQB(this.db, deviceId, data)) - } - - async readDevice(deviceId: DeviceId): Promise { - const row = await device.readQB(this.db, deviceId).executeTakeFirst() - return row ? device.rowToDeviceData(row) : null - } - - async updateDevice( - deviceId: DeviceId, - data: Partial, - ): Promise { - await this.db.executeWithRetry(device.updateQB(this.db, deviceId, data)) - } - - async deleteDevice(deviceId: DeviceId): Promise { - // Will cascade to device_account (device_account_device_id_fk) - await this.db.executeWithRetry(device.removeQB(this.db, deviceId)) - } - - // TokenStore - - async createToken( - id: TokenId, - data: TokenData, - refreshToken?: RefreshToken, - ): Promise { - await this.db.transaction(async (dbTxn) => { - if (refreshToken) { - const { count } = await usedRefreshToken - .countQB(dbTxn, refreshToken) - .executeTakeFirstOrThrow() - - if (count > 0) { - throw new Error('Refresh token already in use') - } - } - - return token.createQB(dbTxn, id, data, refreshToken).execute() - }) - } - - async readToken(tokenId: TokenId): Promise { - const row = await token.findByQB(this.db, { tokenId }).executeTakeFirst() - return row ? token.toTokenInfo(row, this.serviceDid) : null - } - - async deleteToken(tokenId: TokenId): Promise { - // Will cascade to used_refresh_token (used_refresh_token_fk) - await this.db.executeWithRetry(token.removeQB(this.db, tokenId)) - } - - async rotateToken( - tokenId: TokenId, - newTokenId: TokenId, - newRefreshToken: RefreshToken, - newData: NewTokenData, - ): Promise { - const err = await this.db.transaction(async (dbTxn) => { - const { id, currentRefreshToken } = await token - .forRotateQB(dbTxn, tokenId) - .executeTakeFirstOrThrow() - - if (currentRefreshToken) { - await usedRefreshToken - .insertQB(dbTxn, id, currentRefreshToken) - .execute() - } - - const { count } = await usedRefreshToken - .countQB(dbTxn, newRefreshToken) - .executeTakeFirstOrThrow() - - if (count > 0) { - // Do NOT throw (we don't want the transaction to be rolled back) - return new Error('New refresh token already in use') - } - - await token - .rotateQB(dbTxn, id, newTokenId, newRefreshToken, newData) - .execute() - }) - - if (err) throw err - } - - async findTokenByRefreshToken( - refreshToken: RefreshToken, - ): Promise { - const used = await usedRefreshToken - .findByTokenQB(this.db, refreshToken) - .executeTakeFirst() - - const search = used - ? { id: used.tokenId } - : { currentRefreshToken: refreshToken } - - const row = await token.findByQB(this.db, search).executeTakeFirst() - return row ? token.toTokenInfo(row, this.serviceDid) : null - } - - async findTokenByCode(code: Code): Promise { - const row = await token.findByQB(this.db, { code }).executeTakeFirst() - return row ? token.toTokenInfo(row, this.serviceDid) : null - } } diff --git a/packages/pds/src/account-manager/helpers/device-account.ts b/packages/pds/src/account-manager/helpers/device-account.ts index 2c8a5a218..abb703b75 100644 --- a/packages/pds/src/account-manager/helpers/device-account.ts +++ b/packages/pds/src/account-manager/helpers/device-account.ts @@ -114,6 +114,7 @@ export const createOrUpdateQB = ( .insertInto('device_account') .values({ did, deviceId, authorizedClients, ...values }) .onConflict((oc) => oc.columns(['deviceId', 'did']).doUpdateSet(values)) + .returning(['remember', 'authorizedClients', 'authenticatedAt']) } export const getAccountInfoQB = ( diff --git a/packages/pds/src/account-manager/oauth-store.ts b/packages/pds/src/account-manager/oauth-store.ts new file mode 100644 index 000000000..c72d12534 --- /dev/null +++ b/packages/pds/src/account-manager/oauth-store.ts @@ -0,0 +1,494 @@ +import { Client, createOp as createPlcOp } from '@did-plc/lib' +import { Selectable } from 'kysely' +import { Keypair, Secp256k1Keypair } from '@atproto/crypto' +import { + Account, + AccountInfo, + AccountStore, + AuthenticateAccountData, + Code, + DeviceAccountInfo, + DeviceData, + DeviceId, + DeviceStore, + FoundRequestResult, + HandleUnavailableError, + InvalidRequestError, + NewTokenData, + RefreshToken, + RequestData, + RequestId, + RequestStore, + ResetPasswordConfirmData, + ResetPasswordRequestData, + SignUpData, + TokenData, + TokenId, + TokenInfo, + TokenStore, + UpdateRequestData, +} from '@atproto/oauth-provider' +import { + AuthRequiredError as XrpcAuthRequiredError, + InvalidRequestError as XrpcInvalidRequestError, +} from '@atproto/xrpc-server' +import { ActorStore } from '../actor-store/actor-store' +import { BackgroundQueue } from '../background' +import { ImageUrlBuilder } from '../image/image-url-builder' +import { ServerMailer } from '../mailer' +import { Sequencer } from '../sequencer' +import { AccountManager } from './account-manager' +import { AccountStatus, ActorAccount } from './helpers/account' +import * as authRequest from './helpers/authorization-request' +import * as device from './helpers/device' +import * as deviceAccount from './helpers/device-account' +import * as token from './helpers/token' +import * as usedRefreshToken from './helpers/used-refresh-token' + +/** + * This class' purpose is to implement the interface needed by the OAuthProvider + * to interact with the account database (through the {@link AccountManager}). + * + * @note The use of this class assumes that there is no entryway. + */ +export class OAuthStore + implements AccountStore, RequestStore, DeviceStore, TokenStore +{ + constructor( + private readonly accountManager: AccountManager, + private readonly actorStore: ActorStore, + private readonly imageUrlBuilder: ImageUrlBuilder, + private readonly backgroundQueue: BackgroundQueue, + private readonly mailer: ServerMailer, + private readonly sequencer: Sequencer, + private readonly plcClient: Client, + private readonly plcRotationKey: Keypair, + private readonly publicUrl: string, + private readonly recoveryDidKey: string | null, + ) {} + + private get db() { + const { db } = this.accountManager + if (db.destroyed) throw new Error('Database connection is closed') + return db + } + + private get serviceDid() { + return this.accountManager.serviceDid + } + + private async buildAccount(row: Selectable): Promise { + const account = deviceAccount.toAccount(row, this.serviceDid) + + if (!account.name || !account.picture) { + const did = account.sub + + const profile = await this.actorStore.read(did, async (store) => { + return store.record.getProfileRecord() + }) + + if (profile) { + const { avatar, displayName } = profile + + account.name ||= displayName + account.picture ||= avatar + ? this.imageUrlBuilder.build('avatar', did, avatar.ref.toString()) + : undefined + } + } + + return account + } + + private async verifyEmailAvailability(email: string): Promise { + // @NOTE Email validity & disposability check performed by the OAuthProvider + + const account = await this.accountManager.getAccountByEmail(email, { + includeDeactivated: true, + includeTakenDown: true, + }) + + if (account) { + throw new InvalidRequestError(`Email already taken`) + } + } + + // AccountStore + + async createAccount({ + locale: _locale, + inviteCode, + handle, + email, + password, + }: SignUpData): Promise { + // @TODO Send an account creation confirmation email (+verification link) to the user (in their locale) + // @NOTE Password strength already enforced by the OAuthProvider + + await Promise.all([ + this.verifyEmailAvailability(email), + this.verifyHandleAvailability(handle), + !inviteCode || this.accountManager.ensureInviteIsAvailable(inviteCode), + ]) + + // @TODO The code bellow should probably be refactored to be common with the + // code of the `com.atproto.server.createAccount` XRPC endpoint. + + const signingKey = await Secp256k1Keypair.create({ exportable: true }) + const signingKeyDid = signingKey.did() + + const plcCreate = await createPlcOp({ + signingKey: signingKeyDid, + rotationKeys: this.recoveryDidKey + ? [this.recoveryDidKey, this.plcRotationKey.did()] + : [this.plcRotationKey.did()], + handle, + pds: this.publicUrl, + signer: this.plcRotationKey, + }) + + const { did, op } = plcCreate + + await this.actorStore.create(did, signingKey) + try { + const commit = await this.actorStore.transact(did, (actorTxn) => + actorTxn.repo.createRepo([]), + ) + + await this.plcClient.sendOperation(did, op) + + await this.accountManager.createAccount({ + did, + handle, + email, + password, + inviteCode, + repoCid: commit.cid, + repoRev: commit.rev, + }) + try { + await this.sequencer.sequenceIdentityEvt(did, handle) + await this.sequencer.sequenceAccountEvt(did, AccountStatus.Active) + await this.sequencer.sequenceCommit(did, commit) + await this.accountManager.updateRepoRoot(did, commit.cid, commit.rev) + await this.actorStore.clearReservedKeypair(signingKeyDid, did) + + const account = await this.accountManager.getAccount(did) + if (!account) throw new Error('Account not found') + + return await this.buildAccount(account) + } catch (err) { + this.accountManager.deleteAccount(did) + throw err + } + } catch (err) { + await this.actorStore.destroy(did) + throw err + } + } + + async authenticateAccount({ + locale: _locale, + username: identifier, + password, + // Not supported by the PDS (yet?) + emailOtp = undefined, + }: AuthenticateAccountData): Promise { + // @TODO (?) Send an email to the user to notify them of the login attempt + try { + // Should never happen + if (emailOtp != null) { + throw new Error('Email OTP is not supported') + } + + const { user, appPassword, isSoftDeleted } = + await this.accountManager.login({ identifier, password }) + + if (isSoftDeleted) { + throw new InvalidRequestError('Account was taken down') + } + + if (appPassword) { + throw new InvalidRequestError('App passwords are not allowed') + } + + return this.buildAccount(user) + } catch (err) { + if (err instanceof XrpcAuthRequiredError) { + throw new InvalidRequestError(err.message, err) + } + throw err + } + } + + async addDeviceAccount( + deviceId: DeviceId, + sub: string, + remember: boolean, + ): Promise { + const [row] = await this.db.executeWithRetry( + deviceAccount.createOrUpdateQB(this.db, deviceId, sub, remember), + ) + if (!row) throw new Error('Failed to create device account') + return deviceAccount.toDeviceAccountInfo(row) + } + + async addAuthorizedClient( + deviceId: DeviceId, + sub: string, + clientId: string, + ): Promise { + await this.db.transaction(async (dbTxn) => { + const row = await deviceAccount + .readQB(dbTxn, deviceId, sub) + .executeTakeFirstOrThrow() + + const { authorizedClients } = deviceAccount.toDeviceAccountInfo(row) + if (!authorizedClients.includes(clientId)) { + await deviceAccount + .updateQB(dbTxn, deviceId, sub, { + authorizedClients: [...authorizedClients, clientId], + }) + .execute() + } + }) + } + + async getDeviceAccount( + deviceId: DeviceId, + sub: string, + ): Promise { + const row = await deviceAccount + .getAccountInfoQB(this.db, deviceId, sub) + .executeTakeFirst() + + if (!row) return null + + return { + account: await this.buildAccount(row), + info: deviceAccount.toDeviceAccountInfo(row), + } + } + + async listDeviceAccounts(deviceId: DeviceId): Promise { + const rows = await deviceAccount + .listRememberedQB(this.db, deviceId) + .execute() + + return Promise.all( + rows.map(async (row) => ({ + account: await this.buildAccount(row), + info: deviceAccount.toDeviceAccountInfo(row), + })), + ) + } + + async removeDeviceAccount(deviceId: DeviceId, sub: string): Promise { + await this.db.executeWithRetry( + deviceAccount.removeQB(this.db, deviceId, sub), + ) + } + + async resetPasswordRequest({ + locale: _locale, + email, + }: ResetPasswordRequestData): Promise { + const account = await this.accountManager.getAccountByEmail(email, { + includeDeactivated: true, + includeTakenDown: true, + }) + + if (!account?.email || !account?.handle) return + + const { handle } = account + const token = await this.accountManager.createEmailToken( + account.did, + 'reset_password', + ) + + // @TODO Use the locale to send the email in the right language + await this.mailer.sendResetPassword( + { handle, token }, + { to: account.email }, + ) + } + + async resetPasswordConfirm(data: ResetPasswordConfirmData): Promise { + await this.accountManager.resetPassword(data) + } + + async verifyHandleAvailability(handle: string): Promise { + // @NOTE Handle validity & normalization already enforced by the OAuthProvider + try { + const normalized = + await this.accountManager.normalizeAndValidateHandle(handle) + + // Should never happen (OAuthProvider should have already validated the + // handle) This check is just a safeguard against future normalization + // changes. + if (normalized !== handle) { + throw new HandleUnavailableError('syntax', 'Invalid handle') + } + + const account = await this.accountManager.getAccount(normalized, { + includeDeactivated: true, + includeTakenDown: true, + }) + + if (account) { + throw new HandleUnavailableError('taken') + } + } catch (err) { + if (err instanceof XrpcInvalidRequestError) { + throw err.customErrorName === 'HandleNotAvailable' + ? new HandleUnavailableError('taken', err.message) + : new HandleUnavailableError('syntax', err.message) + } + + throw err + } + } + + // RequestStore + + async createRequest(id: RequestId, data: RequestData): Promise { + await this.db.executeWithRetry(authRequest.createQB(this.db, id, data)) + } + + async readRequest(id: RequestId): Promise { + try { + const row = await authRequest.readQB(this.db, id).executeTakeFirst() + if (!row) return null + return authRequest.rowToRequestData(row) + } finally { + // Take the opportunity to clean up expired requests. Do this after we got + // the current (potentially expired) request data to allow the provider to + // handle expired requests. + this.backgroundQueue.add(async () => { + await this.db.executeWithRetry(authRequest.removeOldExpiredQB(this.db)) + }) + } + } + + async updateRequest(id: RequestId, data: UpdateRequestData): Promise { + await this.db.executeWithRetry(authRequest.updateQB(this.db, id, data)) + } + + async deleteRequest(id: RequestId): Promise { + await this.db.executeWithRetry(authRequest.removeByIdQB(this.db, id)) + } + + async findRequestByCode(code: Code): Promise { + const row = await authRequest.findByCodeQB(this.db, code).executeTakeFirst() + return row ? authRequest.rowToFoundRequestResult(row) : null + } + + // DeviceStore + + async createDevice(deviceId: DeviceId, data: DeviceData): Promise { + await this.db.executeWithRetry(device.createQB(this.db, deviceId, data)) + } + + async readDevice(deviceId: DeviceId): Promise { + const row = await device.readQB(this.db, deviceId).executeTakeFirst() + return row ? device.rowToDeviceData(row) : null + } + + async updateDevice( + deviceId: DeviceId, + data: Partial, + ): Promise { + await this.db.executeWithRetry(device.updateQB(this.db, deviceId, data)) + } + + async deleteDevice(deviceId: DeviceId): Promise { + // Will cascade to device_account (device_account_device_id_fk) + await this.db.executeWithRetry(device.removeQB(this.db, deviceId)) + } + + // TokenStore + + async createToken( + id: TokenId, + data: TokenData, + refreshToken?: RefreshToken, + ): Promise { + await this.db.transaction(async (dbTxn) => { + if (refreshToken) { + const { count } = await usedRefreshToken + .countQB(dbTxn, refreshToken) + .executeTakeFirstOrThrow() + + if (count > 0) { + throw new Error('Refresh token already in use') + } + } + + return token.createQB(dbTxn, id, data, refreshToken).execute() + }) + } + + async readToken(tokenId: TokenId): Promise { + const row = await token.findByQB(this.db, { tokenId }).executeTakeFirst() + return row ? token.toTokenInfo(row, this.serviceDid) : null + } + + async deleteToken(tokenId: TokenId): Promise { + // Will cascade to used_refresh_token (used_refresh_token_fk) + await this.db.executeWithRetry(token.removeQB(this.db, tokenId)) + } + + async rotateToken( + tokenId: TokenId, + newTokenId: TokenId, + newRefreshToken: RefreshToken, + newData: NewTokenData, + ): Promise { + const err = await this.db.transaction(async (dbTxn) => { + const { id, currentRefreshToken } = await token + .forRotateQB(dbTxn, tokenId) + .executeTakeFirstOrThrow() + + if (currentRefreshToken) { + await usedRefreshToken + .insertQB(dbTxn, id, currentRefreshToken) + .execute() + } + + const { count } = await usedRefreshToken + .countQB(dbTxn, newRefreshToken) + .executeTakeFirstOrThrow() + + if (count > 0) { + // Do NOT throw (we don't want the transaction to be rolled back) + return new Error('New refresh token already in use') + } + + await token + .rotateQB(dbTxn, id, newTokenId, newRefreshToken, newData) + .execute() + }) + + if (err) throw err + } + + async findTokenByRefreshToken( + refreshToken: RefreshToken, + ): Promise { + const used = await usedRefreshToken + .findByTokenQB(this.db, refreshToken) + .executeTakeFirst() + + const search = used + ? { id: used.tokenId } + : { currentRefreshToken: refreshToken } + + const row = await token.findByQB(this.db, search).executeTakeFirst() + return row ? token.toTokenInfo(row, this.serviceDid) : null + } + + async findTokenByCode(code: Code): Promise { + const row = await token.findByQB(this.db, { code }).executeTakeFirst() + return row ? token.toTokenInfo(row, this.serviceDid) : null + } +} diff --git a/packages/pds/src/api/com/atproto/admin/deleteAccount.ts b/packages/pds/src/api/com/atproto/admin/deleteAccount.ts index 20aa9fee9..9d6885514 100644 --- a/packages/pds/src/api/com/atproto/admin/deleteAccount.ts +++ b/packages/pds/src/api/com/atproto/admin/deleteAccount.ts @@ -1,4 +1,4 @@ -import { AccountStatus } from '../../../../account-manager' +import { AccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' diff --git a/packages/pds/src/api/com/atproto/admin/updateAccountHandle.ts b/packages/pds/src/api/com/atproto/admin/updateAccountHandle.ts index fed059cfb..304df361a 100644 --- a/packages/pds/src/api/com/atproto/admin/updateAccountHandle.ts +++ b/packages/pds/src/api/com/atproto/admin/updateAccountHandle.ts @@ -1,6 +1,5 @@ import { InvalidRequestError } from '@atproto/xrpc-server' import { AppContext } from '../../../../context' -import { normalizeAndValidateHandle } from '../../../../handle' import { Server } from '../../../../lexicon' import { httpLogger } from '../../../../logger' @@ -9,12 +8,13 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.authVerifier.adminToken, handler: async ({ input }) => { const { did } = input.body - const handle = await normalizeAndValidateHandle({ - ctx, - handle: input.body.handle, - did, - allowAnyValid: true, - }) + const handle = await ctx.accountManager.normalizeAndValidateHandle( + input.body.handle, + { + did, + allowAnyValid: true, + }, + ) // Pessimistic check to handle spam: also enforced by updateHandle() and the db. const account = await ctx.accountManager.getAccount(handle, { diff --git a/packages/pds/src/api/com/atproto/identity/resolveHandle.ts b/packages/pds/src/api/com/atproto/identity/resolveHandle.ts index d0639ef43..85539ca19 100644 --- a/packages/pds/src/api/com/atproto/identity/resolveHandle.ts +++ b/packages/pds/src/api/com/atproto/identity/resolveHandle.ts @@ -1,20 +1,11 @@ -import * as ident from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' import { AppContext } from '../../../../context' +import { baseNormalizeAndValidate } from '../../../../handle' import { Server } from '../../../../lexicon' export default function (server: Server, ctx: AppContext) { server.com.atproto.identity.resolveHandle(async ({ params }) => { - let handle: string - try { - handle = ident.normalizeAndEnsureValidHandle(params.handle) - } catch (err) { - if (err instanceof ident.InvalidHandleError) { - throw new InvalidRequestError(err.message, 'InvalidHandle') - } else { - throw err - } - } + const handle = baseNormalizeAndValidate(params.handle) let did: string | undefined const user = await ctx.accountManager.getAccount(handle) diff --git a/packages/pds/src/api/com/atproto/identity/updateHandle.ts b/packages/pds/src/api/com/atproto/identity/updateHandle.ts index af9ce0a82..d1025cd09 100644 --- a/packages/pds/src/api/com/atproto/identity/updateHandle.ts +++ b/packages/pds/src/api/com/atproto/identity/updateHandle.ts @@ -1,7 +1,6 @@ import { DAY, MINUTE } from '@atproto/common' import { InvalidRequestError } from '@atproto/xrpc-server' import { AppContext } from '../../../../context' -import { normalizeAndValidateHandle } from '../../../../handle' import { Server } from '../../../../lexicon' import { ids } from '../../../../lexicon/lexicons' import { httpLogger } from '../../../../logger' @@ -40,11 +39,10 @@ export default function (server: Server, ctx: AppContext) { return } - const handle = await normalizeAndValidateHandle({ - ctx, - handle: input.body.handle, - did: requester, - }) + const handle = await ctx.accountManager.normalizeAndValidateHandle( + input.body.handle, + { did: requester }, + ) // Pessimistic check to handle spam: also enforced by updateHandle() and the db. const account = await ctx.accountManager.getAccount(handle, { diff --git a/packages/pds/src/api/com/atproto/server/createAccount.ts b/packages/pds/src/api/com/atproto/server/createAccount.ts index 58c0dbd84..3a74b09c4 100644 --- a/packages/pds/src/api/com/atproto/server/createAccount.ts +++ b/packages/pds/src/api/com/atproto/server/createAccount.ts @@ -5,12 +5,9 @@ import { DidDocument, MINUTE, check } from '@atproto/common' import { ExportableKeypair, Keypair, Secp256k1Keypair } from '@atproto/crypto' import { AtprotoData, ensureAtpDocument } from '@atproto/identity' import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' -import { AccountStatus } from '../../../../account-manager' +import { AccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' -import { - baseNormalizeAndValidate, - normalizeAndValidateHandle, -} from '../../../../handle' +import { baseNormalizeAndValidate } from '../../../../handle' import { Server } from '../../../../lexicon' import { InputSchema as CreateAccountInput } from '../../../../lexicon/types/com/atproto/server/createAccount' import { safeResolveDidDoc } from './util' @@ -23,6 +20,9 @@ export default function (server: Server, ctx: AppContext) { }, auth: ctx.authVerifier.userServiceAuthOptional, handler: async ({ input, auth, req }) => { + // @NOTE Until this code and the OAuthStore's `createAccount` are + // refactored together, any change made here must be reflected over there. + const requester = auth.credentials?.did ?? null const { did, @@ -60,7 +60,7 @@ export default function (server: Server, ctx: AppContext) { didDoc = await safeResolveDidDoc(ctx, did, true) - creds = await ctx.accountManager.createAccount({ + creds = await ctx.accountManager.createAccountAndSession({ did, handle, email, @@ -183,11 +183,10 @@ const validateInputsForLocalPds = async ( } // normalize & ensure valid handle - const handle = await normalizeAndValidateHandle({ - ctx, - handle: input.handle, - did: input.did, - }) + const handle = await ctx.accountManager.normalizeAndValidateHandle( + input.handle, + { did: input.did }, + ) // check that the invite code still has uses if (ctx.cfg.invites.required && inviteCode) { diff --git a/packages/pds/src/api/com/atproto/server/createSession.ts b/packages/pds/src/api/com/atproto/server/createSession.ts index 8d8cc3a1e..1228aa560 100644 --- a/packages/pds/src/api/com/atproto/server/createSession.ts +++ b/packages/pds/src/api/com/atproto/server/createSession.ts @@ -1,7 +1,7 @@ import { DAY, MINUTE } from '@atproto/common' import { INVALID_HANDLE } from '@atproto/syntax' import { AuthRequiredError } from '@atproto/xrpc-server' -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' import { resultPassthru } from '../../../proxy' diff --git a/packages/pds/src/api/com/atproto/server/deleteAccount.ts b/packages/pds/src/api/com/atproto/server/deleteAccount.ts index 9b3ed017d..afc23d77b 100644 --- a/packages/pds/src/api/com/atproto/server/deleteAccount.ts +++ b/packages/pds/src/api/com/atproto/server/deleteAccount.ts @@ -1,6 +1,6 @@ import { MINUTE } from '@atproto/common' import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' -import { AccountStatus } from '../../../../account-manager' +import { AccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' diff --git a/packages/pds/src/api/com/atproto/server/getSession.ts b/packages/pds/src/api/com/atproto/server/getSession.ts index b1fde5696..9660bbb32 100644 --- a/packages/pds/src/api/com/atproto/server/getSession.ts +++ b/packages/pds/src/api/com/atproto/server/getSession.ts @@ -1,6 +1,6 @@ import { INVALID_HANDLE } from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AuthScope } from '../../../../auth-verifier' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' diff --git a/packages/pds/src/api/com/atproto/server/refreshSession.ts b/packages/pds/src/api/com/atproto/server/refreshSession.ts index e81ae05dc..683202e42 100644 --- a/packages/pds/src/api/com/atproto/server/refreshSession.ts +++ b/packages/pds/src/api/com/atproto/server/refreshSession.ts @@ -1,6 +1,6 @@ import { INVALID_HANDLE } from '@atproto/syntax' import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { softDeleted } from '../../../../db/util' import { Server } from '../../../../lexicon' diff --git a/packages/pds/src/api/com/atproto/sync/getRepoStatus.ts b/packages/pds/src/api/com/atproto/sync/getRepoStatus.ts index 725e2895e..6eb63dd18 100644 --- a/packages/pds/src/api/com/atproto/sync/getRepoStatus.ts +++ b/packages/pds/src/api/com/atproto/sync/getRepoStatus.ts @@ -1,4 +1,4 @@ -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' import { assertRepoAvailability } from './util' diff --git a/packages/pds/src/api/com/atproto/sync/listRepos.ts b/packages/pds/src/api/com/atproto/sync/listRepos.ts index a3a3a7657..6e998c903 100644 --- a/packages/pds/src/api/com/atproto/sync/listRepos.ts +++ b/packages/pds/src/api/com/atproto/sync/listRepos.ts @@ -1,5 +1,5 @@ import { InvalidRequestError } from '@atproto/xrpc-server' -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Cursor, GenericKeyset, paginate } from '../../../../db/pagination' import { Server } from '../../../../lexicon' diff --git a/packages/pds/src/app-view.ts b/packages/pds/src/app-view.ts new file mode 100644 index 000000000..6bcb53f9c --- /dev/null +++ b/packages/pds/src/app-view.ts @@ -0,0 +1,24 @@ +import { format } from 'node:util' +import { AtpAgent } from '@atproto/api' + +export type AppViewOptions = { + url: string + did: string + cdnUrlPattern?: string +} + +export class AppView { + public did: string + public agent: AtpAgent + private cdnUrlPattern?: string + + constructor(options: AppViewOptions) { + this.did = options.did + this.agent = new AtpAgent({ service: options.url }) + this.cdnUrlPattern = options.cdnUrlPattern + } + + getImageUrl(pattern: string, did: string, cid: string): string | undefined { + if (this.cdnUrlPattern) return format(this.cdnUrlPattern, pattern, did, cid) + } +} diff --git a/packages/pds/src/auth-routes.ts b/packages/pds/src/auth-routes.ts index d952f336d..7af2dd64f 100644 --- a/packages/pds/src/auth-routes.ts +++ b/packages/pds/src/auth-routes.ts @@ -1,8 +1,9 @@ import { Router } from 'express' import { oauthProtectedResourceMetadataSchema } from '@atproto/oauth-provider' import { AppContext } from './context' +import { oauthLogger } from './logger' -export const createRouter = ({ authProvider, cfg }: AppContext): Router => { +export const createRouter = ({ oauthProvider, cfg }: AppContext): Router => { const router = Router() const oauthProtectedResourceMetadata = @@ -28,8 +29,13 @@ export const createRouter = ({ authProvider, cfg }: AppContext): Router => { res.status(200).json(oauthProtectedResourceMetadata) }) - if (authProvider) { - router.use(authProvider.createRouter()) + if (oauthProvider) { + const oauthMiddleware = oauthProvider.httpHandler({ + onError: (req, res, err, message) => { + oauthLogger.error({ err, req }, message) + }, + }) + router.use(oauthMiddleware) } return router diff --git a/packages/pds/src/auth-verifier.ts b/packages/pds/src/auth-verifier.ts index 7db671421..16c152050 100644 --- a/packages/pds/src/auth-verifier.ts +++ b/packages/pds/src/auth-verifier.ts @@ -19,7 +19,7 @@ import { parseReqNsid, verifyJwt as verifyServiceJwt, } from '@atproto/xrpc-server' -import { AccountManager } from './account-manager' +import { AccountManager } from './account-manager/account-manager' import { softDeleted } from './db' type ReqCtx = AuthVerifierContext | StreamAuthVerifierContext diff --git a/packages/pds/src/config/config.ts b/packages/pds/src/config/config.ts index 0c5f5d9e9..8658fac62 100644 --- a/packages/pds/src/config/config.ts +++ b/packages/pds/src/config/config.ts @@ -1,7 +1,7 @@ import assert from 'node:assert' import path from 'node:path' import { DAY, HOUR, SECOND } from '@atproto/common' -import { Customization } from '@atproto/oauth-provider' +import { BrandingConfig, HcaptchaConfig } from '@atproto/oauth-provider' import { ServerEnvironment } from './env' // off-config but still from env: @@ -261,38 +261,49 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => { : { issuer: serviceCfg.publicUrl, provider: { - customization: { + hcaptcha: + env.hcaptchaSiteKey && + env.hcaptchaSecretKey && + env.hcaptchaTokenSalt + ? { + siteKey: env.hcaptchaSiteKey, + secretKey: env.hcaptchaSecretKey, + tokenSalt: env.hcaptchaTokenSalt, + } + : undefined, + branding: { name: env.serviceName ?? 'Personal PDS', logo: env.logoUrl, colors: { brand: env.brandColor, error: env.errorColor, warning: env.warningColor, + success: env.successColor, }, links: [ { - title: 'Home', + title: { en: 'Home', fr: 'Accueil' }, href: env.homeUrl, - rel: 'bookmark', + rel: 'canonical' as const, // Prevents login page from being indexed }, { - title: 'Terms of Service', + title: { en: 'Terms of Service' }, href: env.termsOfServiceUrl, - rel: 'terms-of-service', + rel: 'terms-of-service' as const, }, { - title: 'Privacy Policy', + title: { en: 'Privacy Policy' }, href: env.privacyPolicyUrl, - rel: 'privacy-policy', + rel: 'privacy-policy' as const, }, { - title: 'Support', + title: { en: 'Support' }, href: env.supportUrl, - rel: 'help', + rel: 'help' as const, }, ].filter( - (f): f is typeof f & { href: NonNullable<(typeof f)['href']> } => - f.href != null, + (f: T): f is T & { href: string } => + f.href != null && f.href !== '', ), }, }, @@ -435,7 +446,8 @@ export type OAuthConfig = { provider: | false | { - customization: Customization + hcaptcha?: HcaptchaConfig + branding: BrandingConfig } } diff --git a/packages/pds/src/config/env.ts b/packages/pds/src/config/env.ts index 22d2a3934..39d106da0 100644 --- a/packages/pds/src/config/env.ts +++ b/packages/pds/src/config/env.ts @@ -18,10 +18,16 @@ export const readEnv = (): ServerEnvironment => { blobUploadLimit: envInt('PDS_BLOB_UPLOAD_LIMIT'), devMode: envBool('PDS_DEV_MODE'), + // OAuth + hcaptchaSiteKey: envStr('PDS_HCAPTCHA_SITE_KEY'), + hcaptchaSecretKey: envStr('PDS_HCAPTCHA_SECRET_KEY'), + hcaptchaTokenSalt: envStr('PDS_HCAPTCHA_TOKEN_SALT'), + // branding brandColor: envStr('PDS_PRIMARY_COLOR'), errorColor: envStr('PDS_ERROR_COLOR'), warningColor: envStr('PDS_WARNING_COLOR'), + successColor: envStr('PDS_SUCCESS_COLOR'), // database dataDirectory: envStr('PDS_DATA_DIRECTORY'), @@ -150,10 +156,16 @@ export type ServerEnvironment = { blobUploadLimit?: number devMode?: boolean + // OAuth + hcaptchaSiteKey?: string + hcaptchaSecretKey?: string + hcaptchaTokenSalt?: string + // branding brandColor?: string errorColor?: string warningColor?: string + successColor?: string // database dataDirectory?: string diff --git a/packages/pds/src/context.ts b/packages/pds/src/context.ts index a8e7150af..066967f46 100644 --- a/packages/pds/src/context.ts +++ b/packages/pds/src/context.ts @@ -8,7 +8,12 @@ import { AtpAgent } from '@atproto/api' import { KmsKeypair, S3BlobStore } from '@atproto/aws' import * as crypto from '@atproto/crypto' import { IdResolver } from '@atproto/identity' -import { JoseKey, OAuthVerifier } from '@atproto/oauth-provider' +import { + AccessTokenType, + JoseKey, + OAuthProvider, + OAuthVerifier, +} from '@atproto/oauth-provider' import { BlobStore } from '@atproto/repo' import { RateLimiter, @@ -24,7 +29,8 @@ import { safeFetchWrap, unicastLookup, } from '@atproto-labs/fetch-node' -import { AccountManager } from './account-manager' +import { AccountManager } from './account-manager/account-manager' +import { OAuthStore } from './account-manager/oauth-store' import { ActorStore } from './actor-store/actor-store' import { authPassthru, forwardedFor } from './api/proxy' import { @@ -42,7 +48,6 @@ import { ImageUrlBuilder } from './image/image-url-builder' import { fetchLogger } from './logger' import { ServerMailer } from './mailer' import { ModerationMailer } from './mailer/moderation' -import { PdsOAuthProvider } from './oauth/provider' import { LocalViewer, LocalViewerCreator } from './read-after-write/viewer' import { getRedisClient } from './redis' import { Sequencer } from './sequencer' @@ -68,7 +73,7 @@ export type AppContextOptions = { entrywayAgent?: AtpAgent proxyAgent: undici.Dispatcher safeFetch: Fetch - authProvider?: PdsOAuthProvider + oauthProvider?: OAuthProvider authVerifier: AuthVerifier plcRotationKey: crypto.Keypair cfg: ServerConfig @@ -96,7 +101,7 @@ export class AppContext { public proxyAgent: undici.Dispatcher public safeFetch: Fetch public authVerifier: AuthVerifier - public authProvider?: PdsOAuthProvider + public oauthProvider?: OAuthProvider public plcRotationKey: crypto.Keypair public cfg: ServerConfig @@ -122,7 +127,7 @@ export class AppContext { this.proxyAgent = opts.proxyAgent this.safeFetch = opts.safeFetch this.authVerifier = opts.authVerifier - this.authProvider = opts.authProvider + this.oauthProvider = opts.oauthProvider this.plcRotationKey = opts.plcRotationKey this.cfg = opts.cfg } @@ -247,13 +252,11 @@ export class AppContext { }) const accountManager = new AccountManager( - actorStore, - imageUrlBuilder, - backgroundQueue, - cfg.db.accountDbLoc, + idResolver, jwtSecretKey, cfg.service.did, - cfg.db.disableWalAutoCheckpoint, + cfg.identity.serviceHandleDomains, + cfg.db, ) await accountManager.migrateOrThrow() @@ -323,26 +326,43 @@ export class AppContext { logError: false, }) - const authProvider = cfg.oauth.provider - ? new PdsOAuthProvider({ + const oauthProvider = cfg.oauth.provider + ? new OAuthProvider({ issuer: cfg.oauth.issuer, - keyset: [ - // Note: OpenID compatibility would require an RS256 private key in this list - await JoseKey.fromKeyLike(jwtSecretKey, undefined, 'HS256'), - ], - accountManager, + keyset: [await JoseKey.fromKeyLike(jwtSecretKey, undefined, 'HS256')], + store: new OAuthStore( + accountManager, + actorStore, + imageUrlBuilder, + backgroundQueue, + mailer, + sequencer, + plcClient, + plcRotationKey, + cfg.service.publicUrl, + cfg.identity.recoveryDidKey, + ), redis: redisScratch, dpopSecret: secrets.dpopSecret, - customization: cfg.oauth.provider.customization, + inviteCodeRequired: cfg.invites.required, + availableUserDomains: cfg.identity.serviceHandleDomains, + hcaptcha: cfg.oauth.provider.hcaptcha, + branding: cfg.oauth.provider.branding, safeFetch, - // @TODO: Make this configurable. The legacy implementation used to - // blindly trust the X-Forwarded-For header. - trustProxy: (_addr: string, _i: number) => true, + metadata: { + protected_resources: [new URL(cfg.oauth.issuer).origin], + scopes_supported: ['transition:generic', 'transition:chat.bsky'], + }, + // If the PDS is both an authorization server & resource server (no + // entryway), there is no need to use JWTs as access tokens. Instead, + // the PDS can use tokenId as access tokens. This allows the PDS to + // always use up-to-date token data from the token store. + accessTokenType: AccessTokenType.id, }) : undefined const oauthVerifier: OAuthVerifier = - authProvider ?? // OAuthProvider extends OAuthVerifier + oauthProvider ?? // OAuthProvider extends OAuthVerifier new OAuthVerifier({ issuer: cfg.oauth.issuer, keyset: [await JoseKey.fromKeyLike(jwtPublicKey!, undefined, 'ES256K')], @@ -388,7 +408,7 @@ export class AppContext { proxyAgent, safeFetch, authVerifier, - authProvider, + oauthProvider, plcRotationKey, cfg, ...(overrides ?? {}), diff --git a/packages/pds/src/handle/index.ts b/packages/pds/src/handle/index.ts index 1534d51cd..5bf7275c9 100644 --- a/packages/pds/src/handle/index.ts +++ b/packages/pds/src/handle/index.ts @@ -1,61 +1,15 @@ -import * as ident from '@atproto/syntax' +import { + InvalidHandleError, + normalizeAndEnsureValidHandle, +} from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' -import { AppContext } from '../context' -import { hasExplicitSlur } from './explicit-slurs' import { reservedSubdomains } from './reserved' -export const normalizeAndValidateHandle = async (opts: { - ctx: AppContext - handle: string - did?: string - allowAnyValid?: boolean -}): Promise => { - const { ctx, did, allowAnyValid } = opts - // base formatting validation - const handle = baseNormalizeAndValidate(opts.handle) - // tld validation - if (!ident.isValidTld(handle)) { - throw new InvalidRequestError( - 'Handle TLD is invalid or disallowed', - 'InvalidHandle', - ) - } - // slur check - if (!allowAnyValid && hasExplicitSlur(handle)) { - throw new InvalidRequestError( - 'Inappropriate language in handle', - 'InvalidHandle', - ) - } - if (isServiceDomain(handle, ctx.cfg.identity.serviceHandleDomains)) { - // verify constraints on a service domain - ensureHandleServiceConstraints( - handle, - ctx.cfg.identity.serviceHandleDomains, - allowAnyValid, - ) - } else { - if (opts.did === undefined) { - throw new InvalidRequestError( - 'Not a supported handle domain', - 'UnsupportedDomain', - ) - } - // verify resolution of a non-service domain - const resolvedDid = await ctx.idResolver.handle.resolve(handle) - if (resolvedDid !== did) { - throw new InvalidRequestError('External handle did not resolve to DID') - } - } - return handle -} - export const baseNormalizeAndValidate = (handle: string) => { try { - const normalized = ident.normalizeAndEnsureValidHandle(handle) - return normalized + return normalizeAndEnsureValidHandle(handle) } catch (err) { - if (err instanceof ident.InvalidHandleError) { + if (err instanceof InvalidHandleError) { throw new InvalidRequestError(err.message, 'InvalidHandle') } throw err diff --git a/packages/pds/src/image/image-url.ts b/packages/pds/src/image/image-url.ts new file mode 100644 index 000000000..05020f561 --- /dev/null +++ b/packages/pds/src/image/image-url.ts @@ -0,0 +1,16 @@ +import { AppView } from '../app-view' +import { ids } from '../lexicon/lexicons' + +export class ImageUrlBuilder { + constructor( + readonly pdsHostname: string, + readonly appview?: AppView, + ) {} + + build(pattern: string, did: string, cid: string): string { + return ( + this.appview?.getImageUrl(pattern, did, cid) ?? + `https://${this.pdsHostname}/xrpc/${ids.ComAtprotoSyncGetBlob}?did=${did}&cid=${cid}` + ) + } +} diff --git a/packages/pds/src/mailer/index.ts b/packages/pds/src/mailer/index.ts index 8ae5780eb..ceed8184e 100644 --- a/packages/pds/src/mailer/index.ts +++ b/packages/pds/src/mailer/index.ts @@ -6,6 +6,8 @@ import { ServerConfig } from '../config' import { mailerLogger } from '../logger' import * as templates from './templates' +// @TODO Add support for i18n + export class ServerMailer { constructor( public readonly transporter: Transporter, @@ -23,35 +25,35 @@ export class ServerMailer { params: { handle: string; token: string }, mailOpts: Mail.Options, ) { - return this.sendTemplate('resetPassword', params, { + await this.sendTemplate('resetPassword', params, { subject: 'Password Reset Requested', ...mailOpts, }) } async sendAccountDelete(params: { token: string }, mailOpts: Mail.Options) { - return this.sendTemplate('deleteAccount', params, { + await this.sendTemplate('deleteAccount', params, { subject: 'Account Deletion Requested', ...mailOpts, }) } async sendConfirmEmail(params: { token: string }, mailOpts: Mail.Options) { - return this.sendTemplate('confirmEmail', params, { + await this.sendTemplate('confirmEmail', params, { subject: 'Email Confirmation', ...mailOpts, }) } async sendUpdateEmail(params: { token: string }, mailOpts: Mail.Options) { - return this.sendTemplate('updateEmail', params, { + await this.sendTemplate('updateEmail', params, { subject: 'Email Update Requested', ...mailOpts, }) } async sendPlcOperation(params: { token: string }, mailOpts: Mail.Options) { - return this.sendTemplate('plcOperation', params, { + await this.sendTemplate('plcOperation', params, { subject: 'PLC Update Operation Requested', ...mailOpts, }) diff --git a/packages/pds/src/oauth/provider.ts b/packages/pds/src/oauth/provider.ts deleted file mode 100644 index 9f1bf937f..000000000 --- a/packages/pds/src/oauth/provider.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - AccessTokenType, - OAuthProvider, - OAuthProviderOptions, -} from '@atproto/oauth-provider' -import { AccountManager } from '../account-manager/index' -import { oauthLogger } from '../logger' - -export type AuthProviderOptions = { - accountManager: AccountManager -} & Pick< - OAuthProviderOptions, - 'issuer' | 'redis' | 'keyset' | 'dpopSecret' | 'customization' | 'trustProxy' -> & - Required> - -export class PdsOAuthProvider extends OAuthProvider { - constructor({ - accountManager, - keyset, - redis, - dpopSecret, - issuer, - customization, - safeFetch, - trustProxy, - }: AuthProviderOptions) { - super({ - issuer, - keyset, - dpopSecret, - redis, - safeFetch, - customization, - store: accountManager, - trustProxy, - metadata: { - // PdsOAuthProvider is used when the PDS is both an authorization server - // & resource server, in which case the issuer origin is also the - // resource server uri. - protected_resources: [new URL(issuer).origin], - - scopes_supported: ['transition:generic', 'transition:chat.bsky'], - }, - - // If the PDS is both an authorization server & resource server (no - // entryway), there is no need to use JWTs as access tokens. Instead, - // the PDS can use tokenId as access tokens. This allows the PDS to - // always use up-to-date token data from the token store. - accessTokenType: AccessTokenType.id, - }) - } - - createRouter() { - return this.httpHandler({ - onError: (req, res, err, message) => oauthLogger.error({ err }, message), - }) - } -} diff --git a/packages/pds/src/read-after-write/viewer.ts b/packages/pds/src/read-after-write/viewer.ts index c71e20e09..5512ae615 100644 --- a/packages/pds/src/read-after-write/viewer.ts +++ b/packages/pds/src/read-after-write/viewer.ts @@ -1,6 +1,6 @@ import { AtUri, INVALID_HANDLE } from '@atproto/syntax' import { createServiceAuthHeaders } from '@atproto/xrpc-server' -import { AccountManager } from '../account-manager' +import { AccountManager } from '../account-manager/account-manager' import { ActorStoreReader } from '../actor-store/actor-store-reader' import { BskyAppView } from '../bsky-app-view' import { ImageUrlBuilder } from '../image/image-url-builder' diff --git a/packages/pds/src/sequencer/events.ts b/packages/pds/src/sequencer/events.ts index b3be05b07..a0720b395 100644 --- a/packages/pds/src/sequencer/events.ts +++ b/packages/pds/src/sequencer/events.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { cborEncode, noUndefinedVals, schema } from '@atproto/common' import { BlockMap, blocksToCarFile } from '@atproto/repo' -import { AccountStatus } from '../account-manager' +import { AccountStatus } from '../account-manager/account-manager' import { CommitDataWithOps, SyncEvtData } from '../repo' import { RepoSeqInsert } from './db' diff --git a/packages/pds/tests/oauth.test.ts b/packages/pds/tests/oauth.test.ts index 088773d75..37f20a4a1 100644 --- a/packages/pds/tests/oauth.test.ts +++ b/packages/pds/tests/oauth.test.ts @@ -1,33 +1,99 @@ import assert from 'node:assert' import { once } from 'node:events' -import { Server, createServer } from 'node:http' +import { + IncomingMessage, + Server, + ServerResponse, + createServer, +} from 'node:http' import { AddressInfo } from 'node:net' -import { Browser, Page, launch } from 'puppeteer' +import { type Browser, type Page, launch } from 'puppeteer' import { TestNetworkNoAppView } from '@atproto/dev-env' // @ts-expect-error (json file) import files from '@atproto/oauth-client-browser-example' -const getVisibleElement = async (page: Page, selector: string) => { - const elementHandle = await page.waitForSelector(selector) +class PageHelper implements AsyncDisposable { + constructor(protected readonly page: Page) {} - expect(elementHandle).not.toBeNull() - assert(elementHandle) + async goto(url: string) { + await this.page.goto(url) + } - await expect(elementHandle.isVisible()).resolves.toBe(true) + async waitForNetworkIdle() { + await this.page.waitForNetworkIdle() + } - return elementHandle + async navigationAction(run: () => Promise): Promise { + const promise = this.page.waitForNavigation() + await run() + await promise + await this.waitForNetworkIdle() + } + + async checkTitle(expected: string) { + await this.waitForNetworkIdle() + await expect(this.page.title()).resolves.toBe(expected) + } + + async clickOn(selector: string) { + const elementHandle = await this.getVisibleElement(selector) + await elementHandle.click() + return elementHandle + } + + async clickOnButton(text: string) { + return this.clickOn(`button::-p-text(${text})`) + } + + async typeIn(selector: string, text: string) { + const elementHandle = await this.getVisibleElement(selector) + elementHandle.focus() + await elementHandle.type(text) + return elementHandle + } + + async typeInInput(name: string, text: string) { + return this.typeIn(`input[name="${name}"]`, text) + } + + async ensureTextVisibility(text: string, tag = 'p') { + await this.page.waitForSelector(`${tag}::-p-text(${text})`) + } + + protected async getVisibleElement(selector: string) { + const elementHandle = await this.page.waitForSelector(selector) + + expect(elementHandle).not.toBeNull() + assert(elementHandle) + + await expect(elementHandle.isVisible()).resolves.toBe(true) + + return elementHandle + } + + async [Symbol.asyncDispose]() { + return this.page.close() + } + + static async from(browser: Browser) { + const page = await browser.newPage() + return new PageHelper(page) + } } describe('oauth', () => { let browser: Browser let network: TestNetworkNoAppView - let server: Server + let client: Server let appUrl: string beforeAll(async () => { browser = await launch({ browser: 'chrome', + // @NOTE We are using another language than "en" as default language to + // test the language negotiation. + args: ['--accept-lang=fr-BE,en-GB,en'], // For debugging: // headless: false, @@ -47,106 +113,255 @@ describe('oauth', () => { password: 'alice-pass', }) - server = await createClientServer() + client = createServer(clientHandler) + client.listen(0) + await once(client, 'listening') - const { port } = server.address() as AddressInfo + const { port } = client.address() as AddressInfo appUrl = `http://127.0.0.1:${port}?${new URLSearchParams({ plc_directory_url: network.plc.url, handle_resolver: network.pds.url, + sign_up_url: network.pds.url, env: 'test', })}` }) afterAll(async () => { - await server?.close() + await client?.close() await network?.close() await browser?.close() }) - it('starts', async () => { - const page = await browser.newPage() + it('Allows to sign-up trough OAuth', async () => { + const page = await PageHelper.from(browser) await page.goto(appUrl) - await expect(page.title()).resolves.toBe('OAuth Client Example') + await page.checkTitle('OAuth Client Example') - const handleInput = await getVisibleElement( - page, - 'input[placeholder="@handle, DID or PDS url"]', - ) + await page.navigationAction(async () => { + await page.clickOnButton('Sign up') + }) - await handleInput.focus() + await page.checkTitle('Authentification') - await handleInput.type('alice.test') + await page.clickOnButton('Créer un nouveau compte') - await Promise.all([ - // - handleInput.press('Enter'), - page.waitForNavigation(), - ]) + await page.typeInInput('handle', 'bob') - await expect(page.title()).resolves.toBe('Authorize') + await page.clickOnButton('Suivant') - const passwordInput = await getVisibleElement( - page, - 'input[type="password"]', - ) + await page.typeInInput('email', 'bob@test.com') + await page.typeInInput('password', 'bob-pass') - await passwordInput.focus() + await page.clickOnButton("S'inscrire") + + // Make sure the new account is propagated to the PLC directory, allowing + // the client to resolve the account's did + await network.processAll() + + await page.navigationAction(async () => { + await page.clickOnButton("Authoriser l'accès") + }) + + await page.checkTitle('OAuth Client Example') + + await page.ensureTextVisibility('Logged in!') + + await page.clickOnButton('Sign-out') + + await page.waitForNetworkIdle() + + // TODO: Find out why we can't use "using" here + await page[Symbol.asyncDispose]() + }) + + it('allows resetting the password', async () => { + const sendTemplateMock = await withMokedMailer(network) + + const page = await PageHelper.from(browser) + + await page.goto(appUrl) + + await page.checkTitle('OAuth Client Example') + + await page.navigationAction(async () => { + const input = await page.typeIn( + 'input[placeholder="@handle, DID or PDS url"]', + 'alice.test', + ) + + await input.press('Enter') + }) + + await page.checkTitle('Se connecter') + + await page.clickOnButton('Oublié ?') + + await page.checkTitle('Mot de passe oublié') + + await page.typeInInput('email', 'alice@test.com') + + expect(sendTemplateMock).toHaveBeenCalledTimes(0) + + await page.clickOnButton('Suivant') + + await page.checkTitle('Réinitialiser le mot de passe') + + expect(sendTemplateMock).toHaveBeenCalledTimes(1) + + const [templateName, params] = sendTemplateMock.mock.calls[0] + + expect(templateName).toBe('resetPassword') + expect(params).toEqual({ + handle: 'alice.test', + token: expect.any(String), + }) + + const { token } = params as { token: string } + + await page.typeInInput('code', token) + + await page.typeInInput('password', 'alice-new-pass') + + await page.clickOnButton('Suivant') + + await page.checkTitle('Mot de passe mis à jour') + + await page.ensureTextVisibility('Mot de passe mis à jour !', 'h2') + + // TODO: Find out why we can't use "using" here + await page[Symbol.asyncDispose]() + + // TODO: Find out why we can't use "using" here + sendTemplateMock[Symbol.dispose]() + }) + + it('Allows to sign-in trough OAuth', async () => { + const page = await PageHelper.from(browser) + + await page.goto(appUrl) + + await page.checkTitle('OAuth Client Example') + + await page.navigationAction(async () => { + const input = await page.typeIn( + 'input[placeholder="@handle, DID or PDS url"]', + 'alice.test', + ) + + await input.press('Enter') + }) + + await page.checkTitle('Se connecter') + + await page.typeIn('input[type="password"]', 'alice-new-pass') // Make sure the warning is visible - await getVisibleElement(page, 'p::-p-text(Warning)') + await page.ensureTextVisibility('Avertissement') - await passwordInput.type('alice-pass') - - const rememberCheckbox = await getVisibleElement( - page, - 'label::-p-text(Remember this account on this device)', + await page.clickOn( + 'label::-p-text(Se souvenir de ce compte sur cet appareil)', ) - await rememberCheckbox.click() + await page.clickOnButton('Se connecter') - const nextButton = await getVisibleElement(page, 'button::-p-text(Next)') + await page.checkTitle("Authoriser l'accès") - await nextButton.click() + await page.navigationAction(async () => { + await page.clickOnButton("Authoriser l'accès") + }) - const acceptButton = await getVisibleElement( - page, - 'button::-p-text(Accept)', - ) + await page.checkTitle('OAuth Client Example') - await Promise.all([ - // - acceptButton.click(), - page.waitForNavigation(), - ]) + await page.ensureTextVisibility('Logged in!') - await expect(page.title()).resolves.toBe('OAuth Client Example') + await page.clickOnButton('Sign-out') - // Check that the "Logged in!" message is visible - await getVisibleElement(page, 'p::-p-text(Logged in!)') + await page.waitForNetworkIdle() + + // TODO: Find out why we can't use "using" here + await page[Symbol.asyncDispose]() + }) + + it('remembers the session', async () => { + const page = await PageHelper.from(browser) + + await page.goto(appUrl) + + await page.checkTitle('OAuth Client Example') + + await page.navigationAction(async () => { + const input = await page.typeIn( + 'input[placeholder="@handle, DID or PDS url"]', + 'alice.test', + ) + + await input.press('Enter') + }) + + await page.checkTitle("Authoriser l'accès") + + await page.navigationAction(async () => { + await page.clickOnButton("Authoriser l'accès") + }) + + await page.checkTitle('OAuth Client Example') + + await page.ensureTextVisibility('Logged in!') + + await page.clickOnButton('Sign-out') + + await page.waitForNetworkIdle() + + // TODO: Find out why we can't use "using" here + await page[Symbol.asyncDispose]() }) }) -async function createClientServer() { - const server = createServer((req, res) => { - const path = req.url?.split('?')[0].slice(1) || 'index.html' - const file = Object.hasOwn(files, path) ? files[path] : null +async function withMokedMailer(network: TestNetworkNoAppView) { + // @ts-expect-error + const sendTemplateOrig = network.pds.ctx.mailer.sendTemplate + const sendTemplateMock = jest.fn( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async (templateName: unknown, params: unknown, mailOpts: unknown) => { + // + }, + ) as jest.Mock< + Promise, + [templateName: unknown, params: unknown, mailOpts: unknown] + > & + Disposable - if (file) { - res - .writeHead(200, 'OK', { 'content-type': file.type }) - .end(Buffer.from(file.data, 'base64')) - } else { - res - .writeHead(404, 'Not Found', { 'content-type': 'text/plain' }) - .end('Page not found') - } - }) + sendTemplateMock[Symbol.dispose] = () => { + // @ts-expect-error + network.pds.ctx.mailer.sendTemplate = sendTemplateOrig + } - server.listen(0) - await once(server, 'listening') + // @ts-expect-error + network.pds.ctx.mailer.sendTemplate = sendTemplateMock - return server + return sendTemplateMock +} + +function clientHandler( + req: IncomingMessage, + res: ServerResponse, + next?: (err?: unknown) => void, +): void { + const path = req.url?.split('?')[0].slice(1) || 'index.html' + const file = Object.hasOwn(files, path) ? files[path] : null + + if (file) { + res + .writeHead(200, 'OK', { 'content-type': file.type }) + .end(Buffer.from(file.data, 'base64')) + } else if (next) { + next() + } else { + res + .writeHead(404, 'Not Found', { 'content-type': 'text/plain' }) + .end('Page not found') + } } diff --git a/packages/pds/tests/sync/subscribe-repos.test.ts b/packages/pds/tests/sync/subscribe-repos.test.ts index dfceac52c..b1aad0a1b 100644 --- a/packages/pds/tests/sync/subscribe-repos.test.ts +++ b/packages/pds/tests/sync/subscribe-repos.test.ts @@ -14,7 +14,7 @@ import * as repo from '@atproto/repo' import { readCar } from '@atproto/repo' import { ErrorFrame, Frame, MessageFrame, byFrame } from '@atproto/xrpc-server' import { AppContext } from '../../src' -import { AccountStatus } from '../../src/account-manager' +import { AccountStatus } from '../../src/account-manager/account-manager' import { Account as AccountEvt, Commit as CommitEvt, diff --git a/packages/syntax/src/handle.ts b/packages/syntax/src/handle.ts index 8c3cacbc8..35dfb3b84 100644 --- a/packages/syntax/src/handle.ts +++ b/packages/syntax/src/handle.ts @@ -115,6 +115,9 @@ export const isValidTld = (handle: string): boolean => { } export class InvalidHandleError extends Error {} +/** @deprecated Never used */ export class ReservedHandleError extends Error {} +/** @deprecated Never used */ export class UnsupportedDomainError extends Error {} +/** @deprecated Never used */ export class DisallowedDomainError extends Error {} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 287e527df..eb58950f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -535,7 +535,7 @@ importers: devDependencies: '@swc/jest': specifier: ^0.2.24 - version: 0.2.24(@swc/core@1.3.42) + version: 0.2.24(@swc/core@1.11.4) jest: specifier: ^28.1.2 version: 28.1.2(@types/node@18.19.67)(ts-node@10.8.2) @@ -604,10 +604,6 @@ importers: '@atproto-labs/pipe': specifier: workspace:* version: link:../pipe - optionalDependencies: - zod: - specifier: ^3.23.8 - version: 3.23.8 devDependencies: typescript: specifier: ^5.6.3 @@ -944,21 +940,18 @@ importers: '@rollup/plugin-node-resolve': specifier: ^15.2.3 version: 15.2.3(rollup@4.18.0) - '@rollup/plugin-replace': - specifier: ^5.0.5 - version: 5.0.5(rollup@4.18.0) - '@rollup/plugin-terser': - specifier: ^0.4.4 - version: 0.4.4(rollup@4.18.0) - '@rollup/plugin-typescript': - specifier: ^11.1.6 - version: 11.1.6(rollup@4.18.0)(typescript@5.6.3) + '@rollup/plugin-swc': + specifier: ^0.4.0 + version: 0.4.0(@swc/core@1.11.4)(rollup@4.18.0) + '@swc/helpers': + specifier: ^0.5.15 + version: 0.5.15 '@types/react': - specifier: ^18.2.50 - version: 18.3.2 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.2.18 - version: 18.3.0 + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) autoprefixer: specifier: ^10.4.17 version: 10.4.19(postcss@8.4.38) @@ -966,11 +959,11 @@ importers: specifier: ^8.4.33 version: 8.4.38 react: - specifier: ^18.2.0 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.0.0 react-dom: - specifier: ^18.2.0 - version: 18.3.1(react@18.3.1) + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) rollup: specifier: ^4.13.0 version: 4.18.0 @@ -1053,6 +1046,9 @@ importers: '@hapi/accept': specifier: ^6.0.3 version: 6.0.3 + '@hapi/address': + specifier: ^5.1.1 + version: 5.1.1 '@hapi/bourne': specifier: ^3.0.0 version: 3.0.0 @@ -1062,6 +1058,9 @@ importers: cookie: specifier: ^0.6.0 version: 0.6.0 + disposable-email-domains-js: + specifier: ^1.5.0 + version: 1.5.0 forwarded: specifier: ^0.2.0 version: 0.2.0 @@ -1084,21 +1083,42 @@ importers: '@atproto-labs/rollup-plugin-bundle-manifest': specifier: workspace:* version: link:../../internal/rollup-plugin-bundle-manifest + '@hcaptcha/react-hcaptcha': + specifier: ^1.11.2 + version: 1.11.2(react-dom@19.0.0)(react@19.0.0) + '@lingui/cli': + specifier: ^5.2.0 + version: 5.2.0(typescript@5.6.3) + '@lingui/core': + specifier: ^5.2.0 + version: 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + '@lingui/react': + specifier: ^5.2.0 + version: 5.2.0(react@19.0.0) + '@lingui/swc-plugin': + specifier: ^5.4.0 + version: 5.4.0(@lingui/core@5.2.0)(@swc/core@1.10.18) + '@lingui/vite-plugin': + specifier: ^5.2.0 + version: 5.2.0(typescript@5.6.3)(vite@6.2.0) '@rollup/plugin-commonjs': - specifier: ^25.0.7 - version: 25.0.8(rollup@4.18.0) + specifier: ^28.0.2 + version: 28.0.2(rollup@4.18.0) + '@rollup/plugin-dynamic-import-vars': + specifier: ^2.1.5 + version: 2.1.5(rollup@4.18.0) '@rollup/plugin-node-resolve': - specifier: ^15.2.3 - version: 15.2.3(rollup@4.18.0) - '@rollup/plugin-replace': - specifier: ^5.0.5 - version: 5.0.5(rollup@4.18.0) - '@rollup/plugin-terser': - specifier: ^0.4.4 - version: 0.4.4(rollup@4.18.0) - '@rollup/plugin-typescript': - specifier: ^11.1.6 - version: 11.1.6(rollup@4.18.0)(typescript@5.6.3) + specifier: ^16.0.0 + version: 16.0.0(rollup@4.18.0) + '@rollup/plugin-swc': + specifier: ^0.4.0 + version: 0.4.0(@swc/core@1.10.18)(rollup@4.18.0) + '@swc/core': + specifier: ^1.10.18 + version: 1.10.18(@swc/helpers@0.5.15) + '@swc/helpers': + specifier: ^0.5.15 + version: 0.5.15 '@types/cookie': specifier: ^0.6.0 version: 0.6.0 @@ -1109,14 +1129,17 @@ importers: specifier: 1.1.3 version: 1.1.3 '@types/react': - specifier: ^18.2.50 - version: 18.3.2 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.2.18 - version: 18.3.0 + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) '@types/send': specifier: ^0.17.4 version: 0.17.4 + '@vitejs/plugin-react-swc': + specifier: ^3.8.0 + version: 3.8.0(@swc/helpers@0.5.15)(vite@6.2.0) '@web/rollup-plugin-import-meta-assets': specifier: ^2.2.1 version: 2.2.1(rollup@4.18.0) @@ -1124,14 +1147,17 @@ importers: specifier: ^10.4.17 version: 10.4.19(postcss@8.4.38) postcss: - specifier: ^8.4.33 + specifier: ^8.4.38 version: 8.4.38 react: - specifier: ^18.2.0 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.0.0 react-dom: - specifier: ^18.2.0 - version: 18.3.1(react@18.3.1) + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + react-error-boundary: + specifier: ^5.0.0 + version: 5.0.0(react@19.0.0) rollup: specifier: ^4.13.0 version: 4.18.0 @@ -1139,11 +1165,14 @@ importers: specifier: ^4.0.2 version: 4.0.2(postcss@8.4.38) tailwindcss: - specifier: ^3.4.1 + specifier: ^3.4.3 version: 3.4.3 typescript: specifier: ^5.6.3 version: 5.6.3 + vite: + specifier: ^6.2.0 + version: 6.2.0(@types/node@18.19.67) packages/oauth/oauth-types: dependencies: @@ -1870,7 +1899,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/abort-controller@3.224.0: @@ -1878,7 +1907,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/abort-controller@3.257.0: @@ -1886,7 +1915,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/abort-controller@3.374.0: @@ -1902,13 +1931,13 @@ packages: resolution: {integrity: sha512-JeOZ95PW+fJ6bbuqPySYqLqHk1n4+4ueEEraJsiUrPBV0S1ZtyvOGHcnGztKUjr2PYNaiexmpWuvUve9K12HRA==} dependencies: '@aws-sdk/util-base64': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/chunked-blob-reader@3.188.0: resolution: {integrity: sha512-zkPRFZZPL3eH+kH86LDYYXImiClA1/sW60zYOjse9Pgka+eDJlvBN6hcYxwDEKjcwATYiSRR1aVQHcfCinlGXg==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/client-cloudfront@3.261.0: @@ -2099,7 +2128,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.224.0 '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2139,7 +2168,7 @@ packages: '@aws-sdk/util-user-agent-browser': 3.257.0 '@aws-sdk/util-user-agent-node': 3.259.0 '@aws-sdk/util-utf8': 3.254.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2180,7 +2209,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.193.0 '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2220,7 +2249,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.224.0 '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2260,7 +2289,7 @@ packages: '@aws-sdk/util-user-agent-browser': 3.257.0 '@aws-sdk/util-user-agent-node': 3.259.0 '@aws-sdk/util-utf8': 3.254.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2305,7 +2334,7 @@ packages: '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.188.0 fast-xml-parser: 4.0.11 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2349,7 +2378,7 @@ packages: '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.208.0 fast-xml-parser: 4.0.11 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2393,7 +2422,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.259.0 '@aws-sdk/util-utf8': 3.254.0 fast-xml-parser: 4.0.11 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2406,7 +2435,7 @@ packages: '@aws-sdk/types': 3.193.0 '@aws-sdk/util-config-provider': 3.188.0 '@aws-sdk/util-middleware': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/config-resolver@3.224.0: @@ -2417,7 +2446,7 @@ packages: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/config-resolver@3.259.0: @@ -2428,7 +2457,7 @@ packages: '@aws-sdk/types': 3.257.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-env@3.193.0: @@ -2437,7 +2466,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-env@3.224.0: @@ -2446,7 +2475,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-env@3.257.0: @@ -2455,7 +2484,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-imds@3.193.0: @@ -2466,7 +2495,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 '@aws-sdk/url-parser': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-imds@3.224.0: @@ -2477,7 +2506,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/url-parser': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-imds@3.259.0: @@ -2488,7 +2517,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 '@aws-sdk/url-parser': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-ini@3.196.0: @@ -2502,7 +2531,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2518,7 +2547,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2535,7 +2564,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2553,7 +2582,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2571,7 +2600,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2589,7 +2618,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2601,7 +2630,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-process@3.224.0: @@ -2611,7 +2640,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-process@3.257.0: @@ -2621,7 +2650,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-sso@3.196.0: @@ -2632,7 +2661,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2646,7 +2675,7 @@ packages: '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/token-providers': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2660,7 +2689,7 @@ packages: '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/token-providers': 3.261.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2671,7 +2700,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-web-identity@3.224.0: @@ -2680,7 +2709,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-web-identity@3.257.0: @@ -2689,7 +2718,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-codec@3.224.0: @@ -2698,7 +2727,7 @@ packages: '@aws-crypto/crc32': 2.0.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-hex-encoding': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-serde-browser@3.224.0: @@ -2707,7 +2736,7 @@ packages: dependencies: '@aws-sdk/eventstream-serde-universal': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-serde-config-resolver@3.224.0: @@ -2715,7 +2744,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-serde-node@3.224.0: @@ -2724,7 +2753,7 @@ packages: dependencies: '@aws-sdk/eventstream-serde-universal': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-serde-universal@3.224.0: @@ -2733,7 +2762,7 @@ packages: dependencies: '@aws-sdk/eventstream-codec': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/fetch-http-handler@3.193.0: @@ -2743,7 +2772,7 @@ packages: '@aws-sdk/querystring-builder': 3.193.0 '@aws-sdk/types': 3.193.0 '@aws-sdk/util-base64-browser': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/fetch-http-handler@3.224.0: @@ -2753,7 +2782,7 @@ packages: '@aws-sdk/querystring-builder': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-base64': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/fetch-http-handler@3.257.0: @@ -2763,7 +2792,7 @@ packages: '@aws-sdk/querystring-builder': 3.257.0 '@aws-sdk/types': 3.257.0 '@aws-sdk/util-base64': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-blob-browser@3.224.0: @@ -2772,7 +2801,7 @@ packages: '@aws-sdk/chunked-blob-reader': 3.188.0 '@aws-sdk/chunked-blob-reader-native': 3.208.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-node@3.193.0: @@ -2781,7 +2810,7 @@ packages: dependencies: '@aws-sdk/types': 3.193.0 '@aws-sdk/util-buffer-from': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-node@3.224.0: @@ -2790,7 +2819,7 @@ packages: dependencies: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-node@3.257.0: @@ -2800,7 +2829,7 @@ packages: '@aws-sdk/types': 3.257.0 '@aws-sdk/util-buffer-from': 3.208.0 '@aws-sdk/util-utf8': 3.254.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-stream-node@3.224.0: @@ -2808,42 +2837,42 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/invalid-dependency@3.193.0: resolution: {integrity: sha512-54DCknekLwJAI1os76XJ8XCzfAH7BGkBGtlWk5WCNkZTfj3rf5RUiXz4uoKUMWE1rZmyMDoDDS1PBo+yTVKW5w==} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/invalid-dependency@3.224.0: resolution: {integrity: sha512-6huV8LBYQYx84uMhQ2SS7nqEkhTkAufwhKceXnysrcrLDuUmyth09Y7fcFblFIDTr4wTgSI0mf6DKVF4nqYCwQ==} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/invalid-dependency@3.257.0: resolution: {integrity: sha512-T68SAPRNMEhpke0wlxURgogL7q0B8dfqZsSeS20BVR/lksJxLse9+pbmCDxiu1RrXoEIsEwl5rbLN+Hw8BFFYw==} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/is-array-buffer@3.188.0: resolution: {integrity: sha512-n69N4zJZCNd87Rf4NzufPzhactUeM877Y0Tp/F3KiHqGeTnVjYUa4Lv1vLBjqtfjYb2HWT3NKlYn5yzrhaEwiQ==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/is-array-buffer@3.201.0: resolution: {integrity: sha512-UPez5qLh3dNgt0DYnPD/q0mVJY84rA17QE26hVNOW3fAji8W2wrwrxdacWOxyXvlxWsVRcKmr+lay1MDqpAMfg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/lib-storage@3.226.0(@aws-sdk/abort-controller@3.374.0)(@aws-sdk/client-s3@3.224.0): @@ -2869,7 +2898,7 @@ packages: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-bucket-endpoint@3.224.0: @@ -2880,7 +2909,7 @@ packages: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-arn-parser': 3.208.0 '@aws-sdk/util-config-provider': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-content-length@3.193.0: @@ -2889,7 +2918,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-content-length@3.224.0: @@ -2898,7 +2927,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-content-length@3.257.0: @@ -2907,7 +2936,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-endpoint@3.193.0: @@ -2921,7 +2950,7 @@ packages: '@aws-sdk/url-parser': 3.193.0 '@aws-sdk/util-config-provider': 3.188.0 '@aws-sdk/util-middleware': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-endpoint@3.224.0: @@ -2935,7 +2964,7 @@ packages: '@aws-sdk/url-parser': 3.224.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-endpoint@3.226.0: @@ -2949,7 +2978,7 @@ packages: '@aws-sdk/url-parser': 3.226.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-endpoint@3.257.0: @@ -2963,7 +2992,7 @@ packages: '@aws-sdk/url-parser': 3.257.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-expect-continue@3.224.0: @@ -2972,7 +3001,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-flexible-checksums@3.224.0: @@ -2984,7 +3013,7 @@ packages: '@aws-sdk/is-array-buffer': 3.201.0 '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-host-header@3.193.0: @@ -2993,7 +3022,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-host-header@3.224.0: @@ -3002,7 +3031,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-host-header@3.257.0: @@ -3011,7 +3040,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-location-constraint@3.224.0: @@ -3019,7 +3048,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-logger@3.193.0: @@ -3027,7 +3056,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-logger@3.224.0: @@ -3035,7 +3064,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-logger@3.257.0: @@ -3043,7 +3072,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-recursion-detection@3.193.0: @@ -3052,7 +3081,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-recursion-detection@3.224.0: @@ -3061,7 +3090,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-recursion-detection@3.257.0: @@ -3070,7 +3099,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-retry@3.193.0: @@ -3081,7 +3110,7 @@ packages: '@aws-sdk/service-error-classification': 3.193.0 '@aws-sdk/types': 3.193.0 '@aws-sdk/util-middleware': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 uuid: 8.3.2 dev: false @@ -3093,7 +3122,7 @@ packages: '@aws-sdk/service-error-classification': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-middleware': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 uuid: 8.3.2 dev: false @@ -3106,7 +3135,7 @@ packages: '@aws-sdk/types': 3.257.0 '@aws-sdk/util-middleware': 3.257.0 '@aws-sdk/util-retry': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 uuid: 8.3.2 dev: false @@ -3118,7 +3147,7 @@ packages: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-arn-parser': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-sdk-sts@3.193.0: @@ -3130,7 +3159,7 @@ packages: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/signature-v4': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-sdk-sts@3.224.0: @@ -3142,7 +3171,7 @@ packages: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/signature-v4': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-sdk-sts@3.257.0: @@ -3154,7 +3183,7 @@ packages: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/signature-v4': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-serde@3.193.0: @@ -3162,7 +3191,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-serde@3.224.0: @@ -3170,7 +3199,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-serde@3.226.0: @@ -3178,7 +3207,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-serde@3.257.0: @@ -3186,7 +3215,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-signing@3.193.0: @@ -3198,7 +3227,7 @@ packages: '@aws-sdk/signature-v4': 3.193.0 '@aws-sdk/types': 3.193.0 '@aws-sdk/util-middleware': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-signing@3.224.0: @@ -3210,7 +3239,7 @@ packages: '@aws-sdk/signature-v4': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-middleware': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-signing@3.257.0: @@ -3222,7 +3251,7 @@ packages: '@aws-sdk/signature-v4': 3.257.0 '@aws-sdk/types': 3.257.0 '@aws-sdk/util-middleware': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-ssec@3.224.0: @@ -3230,35 +3259,35 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-stack@3.193.0: resolution: {integrity: sha512-Ix5d7gE6bZwFNIVf0dGnjYuymz1gjitNoAZDPpv1nEZlUMek/jcno5lmzWFzUZXY/azpbIyaPwq/wm/c69au5A==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-stack@3.224.0: resolution: {integrity: sha512-8mBrc3nj4h6FnDWnxbjfFXUPr/7UIAaGAG15D27Z/KNFnMjOqNTtpkbcoh3QQHRLX3PjTuvzT5WCqXmgD2/oiw==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-stack@3.226.0: resolution: {integrity: sha512-85wF29LvPvpoed60fZGDYLwv1Zpd/cM0C22WSSFPw1SSJeqO4gtFYyCg2squfT3KI6kF43IIkOCJ+L7GtryPug==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-stack@3.257.0: resolution: {integrity: sha512-awg2F0SvwACBaw4HIObK8pQGfSqAc4Vy+YFzWSfZNVC35oRO6RsRdKHVU99lRC0LrT2Ptmfghl2DMPSrRDbvlQ==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-user-agent@3.193.0: @@ -3267,7 +3296,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-user-agent@3.224.0: @@ -3276,7 +3305,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-user-agent@3.257.0: @@ -3285,7 +3314,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-config-provider@3.193.0: @@ -3295,7 +3324,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-config-provider@3.224.0: @@ -3305,7 +3334,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-config-provider@3.259.0: @@ -3315,7 +3344,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-http-handler@3.193.0: @@ -3326,7 +3355,7 @@ packages: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/querystring-builder': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-http-handler@3.224.0: @@ -3337,7 +3366,7 @@ packages: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/querystring-builder': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-http-handler@3.257.0: @@ -3348,7 +3377,7 @@ packages: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/querystring-builder': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/property-provider@3.193.0: @@ -3356,7 +3385,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/property-provider@3.224.0: @@ -3364,7 +3393,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/property-provider@3.257.0: @@ -3372,7 +3401,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/protocol-http@3.193.0: @@ -3380,7 +3409,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/protocol-http@3.224.0: @@ -3388,7 +3417,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/protocol-http@3.226.0: @@ -3396,7 +3425,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/protocol-http@3.257.0: @@ -3404,7 +3433,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-builder@3.193.0: @@ -3413,7 +3442,7 @@ packages: dependencies: '@aws-sdk/types': 3.193.0 '@aws-sdk/util-uri-escape': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-builder@3.224.0: @@ -3422,7 +3451,7 @@ packages: dependencies: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-uri-escape': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-builder@3.257.0: @@ -3431,7 +3460,7 @@ packages: dependencies: '@aws-sdk/types': 3.257.0 '@aws-sdk/util-uri-escape': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-parser@3.193.0: @@ -3439,7 +3468,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-parser@3.224.0: @@ -3447,7 +3476,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-parser@3.226.0: @@ -3455,7 +3484,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-parser@3.257.0: @@ -3463,7 +3492,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/service-error-classification@3.193.0: @@ -3486,7 +3515,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/shared-ini-file-loader@3.224.0: @@ -3494,7 +3523,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/shared-ini-file-loader@3.257.0: @@ -3502,7 +3531,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4-multi-region@3.224.0: @@ -3518,7 +3547,7 @@ packages: '@aws-sdk/signature-v4': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-arn-parser': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4@3.193.0: @@ -3530,7 +3559,7 @@ packages: '@aws-sdk/util-hex-encoding': 3.188.0 '@aws-sdk/util-middleware': 3.193.0 '@aws-sdk/util-uri-escape': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4@3.224.0: @@ -3542,7 +3571,7 @@ packages: '@aws-sdk/util-hex-encoding': 3.201.0 '@aws-sdk/util-middleware': 3.224.0 '@aws-sdk/util-uri-escape': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4@3.226.0: @@ -3554,7 +3583,7 @@ packages: '@aws-sdk/util-hex-encoding': 3.201.0 '@aws-sdk/util-middleware': 3.226.0 '@aws-sdk/util-uri-escape': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4@3.257.0: @@ -3567,7 +3596,7 @@ packages: '@aws-sdk/util-middleware': 3.257.0 '@aws-sdk/util-uri-escape': 3.201.0 '@aws-sdk/util-utf8': 3.254.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/smithy-client@3.193.0: @@ -3576,7 +3605,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/smithy-client@3.224.0: @@ -3585,7 +3614,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/smithy-client@3.226.0: @@ -3594,7 +3623,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.226.0 '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/smithy-client@3.261.0: @@ -3603,7 +3632,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/token-providers@3.224.0: @@ -3614,7 +3643,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -3627,7 +3656,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -3646,14 +3675,14 @@ packages: resolution: {integrity: sha512-MmmNHrWeO4man7wpOwrAhXlevqtOV9ZLcH4RhnG5LmRce0RFOApx24HoKENfFCcOyCm5LQBlsXCqi0dZWDWU0A==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/types@3.257.0: resolution: {integrity: sha512-LmqXuBQBGeaGi/3Rp7XiEX1B5IPO2UUfBVvu0wwGqVsmstT0SbOVDZGPmxygACbm64n+PRx3uTSDefRfoiWYZg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/url-parser@3.193.0: @@ -3661,7 +3690,7 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/url-parser@3.224.0: @@ -3669,7 +3698,7 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/url-parser@3.226.0: @@ -3677,7 +3706,7 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.226.0 '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/url-parser@3.257.0: @@ -3685,20 +3714,20 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-arn-parser@3.208.0: resolution: {integrity: sha512-QV4af+kscova9dv4VuHOgH8wEr/IIYHDGcnyVtkUEqahCejWr1Kuk+SBK0xMwnZY5LSycOtQ8aeqHOn9qOjZtA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-base64-browser@3.188.0: resolution: {integrity: sha512-qlH+5NZBLiyKziL335BEPedYxX6j+p7KFRWXvDQox9S+s+gLCayednpK+fteOhBenCcR9fUZOVuAPScy1I8qCg==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-base64-node@3.188.0: @@ -3706,7 +3735,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-base64@3.208.0: @@ -3714,27 +3743,27 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-body-length-browser@3.188.0: resolution: {integrity: sha512-8VpnwFWXhnZ/iRSl9mTf+VKOX9wDE8QtN4bj9pBfxwf90H1X7E8T6NkiZD3k+HubYf2J94e7DbeHs7fuCPW5Qg==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-body-length-node@3.188.0: resolution: {integrity: sha512-XwqP3vxk60MKp4YDdvDeCD6BPOiG2e+/Ou4AofZOy5/toB6NKz2pFNibQIUg2+jc7mPMnGnvOW3MQEgSJ+gu/Q==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-body-length-node@3.208.0: resolution: {integrity: sha512-3zj50e5g7t/MQf53SsuuSf0hEELzMtD8RX8C76f12OSRo2Bca4FLLYHe0TZbxcfQHom8/hOaeZEyTyMogMglqg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-buffer-from@3.188.0: @@ -3742,7 +3771,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/is-array-buffer': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-buffer-from@3.208.0: @@ -3750,21 +3779,21 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/is-array-buffer': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-config-provider@3.188.0: resolution: {integrity: sha512-LBA7tLbi7v4uvbOJhSnjJrxbcRifKK/1ZVK94JTV2MNSCCyNkFotyEI5UWDl10YKriTIUyf7o5cakpiDZ3O4xg==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-config-provider@3.208.0: resolution: {integrity: sha512-DSRqwrERUsT34ug+anlMBIFooBEGwM8GejC7q00Y/9IPrQy50KnG5PW2NiTjuLKNi7pdEOlwTSEocJE15eDZIg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-browser@3.193.0: @@ -3774,7 +3803,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-browser@3.224.0: @@ -3784,7 +3813,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-browser@3.261.0: @@ -3794,7 +3823,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-node@3.193.0: @@ -3806,7 +3835,7 @@ packages: '@aws-sdk/node-config-provider': 3.193.0 '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-node@3.224.0: @@ -3818,7 +3847,7 @@ packages: '@aws-sdk/node-config-provider': 3.224.0 '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-node@3.261.0: @@ -3830,7 +3859,7 @@ packages: '@aws-sdk/node-config-provider': 3.259.0 '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-endpoints@3.196.0: @@ -3838,7 +3867,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-endpoints@3.224.0: @@ -3846,7 +3875,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-endpoints@3.257.0: @@ -3854,56 +3883,56 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-hex-encoding@3.188.0: resolution: {integrity: sha512-QyWovTtjQ2RYxqVM+STPh65owSqzuXURnfoof778spyX4iQ4z46wOge1YV2ZtwS8w5LWd9eeVvDrLu5POPYOnA==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-hex-encoding@3.201.0: resolution: {integrity: sha512-7t1vR1pVxKx0motd3X9rI3m/xNp78p3sHtP5yo4NP4ARpxyJ0fokBomY8ScaH2D/B+U5o9ARxldJUdMqyBlJcA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-locate-window@3.310.0: resolution: {integrity: sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-middleware@3.193.0: resolution: {integrity: sha512-+aC6pmkcGgpxaMWCH/FXTsGWl2W342oQGs1OYKGi+W8z9UguXrqamWjdkdMqgunvj9qOEG2KBMKz1FWFFZlUyA==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-middleware@3.224.0: resolution: {integrity: sha512-yA20k9sJdFgs7buVilWExUSJ/Ecr5UJRNQlmgzIpBo9kh5x/N8WyB4kN5MQw5UAA1UZ+j3jmA9+YLFT/mbX3IQ==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-middleware@3.226.0: resolution: {integrity: sha512-B96CQnwX4gRvQdaQkdUtqvDPkrptV5+va6FVeJOocU/DbSYMAScLxtR3peMS8cnlOT6nL1Eoa42OI9AfZz1VwQ==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-middleware@3.257.0: resolution: {integrity: sha512-F9ieon8B8eGVs5tyZtAIG3DZEObDvujkspho0qRbUTHUosM0ylJLsMU800fmC/uRHLRrZvb/RSp59+kNDwSAMw==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-retry@3.257.0: @@ -3911,7 +3940,7 @@ packages: engines: {node: '>= 14.0.0'} dependencies: '@aws-sdk/service-error-classification': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-stream-browser@3.224.0: @@ -3922,7 +3951,7 @@ packages: '@aws-sdk/util-base64': 3.208.0 '@aws-sdk/util-hex-encoding': 3.201.0 '@aws-sdk/util-utf8-browser': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-stream-node@3.224.0: @@ -3932,21 +3961,21 @@ packages: '@aws-sdk/node-http-handler': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-uri-escape@3.188.0: resolution: {integrity: sha512-4Y6AYZMT483Tiuq8dxz5WHIiPNdSFPGrl6tRTo2Oi2FcwypwmFhqgEGcqxeXDUJktvaCBxeA08DLr/AemVhPCg==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-uri-escape@3.201.0: resolution: {integrity: sha512-TeTWbGx4LU2c5rx0obHeDFeO9HvwYwQtMh1yniBz00pQb6Qt6YVOETVQikRZ+XRQwEyCg/dA375UplIpiy54mA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-browser@3.193.0: @@ -3954,7 +3983,7 @@ packages: dependencies: '@aws-sdk/types': 3.193.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-browser@3.224.0: @@ -3962,7 +3991,7 @@ packages: dependencies: '@aws-sdk/types': 3.224.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-browser@3.257.0: @@ -3970,7 +3999,7 @@ packages: dependencies: '@aws-sdk/types': 3.257.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-node@3.193.0: @@ -3984,7 +4013,7 @@ packages: dependencies: '@aws-sdk/node-config-provider': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-node@3.224.0: @@ -3998,7 +4027,7 @@ packages: dependencies: '@aws-sdk/node-config-provider': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-node@3.259.0: @@ -4012,19 +4041,19 @@ packages: dependencies: '@aws-sdk/node-config-provider': 3.259.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8-browser@3.188.0: resolution: {integrity: sha512-jt627x0+jE+Ydr9NwkFstg3cUvgWh56qdaqAMDsqgRlKD21md/6G226z/Qxl7lb1VEW2LlmCx43ai/37Qwcj2Q==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8-browser@3.259.0: resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8-node@3.188.0: @@ -4032,7 +4061,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8-node@3.208.0: @@ -4040,7 +4069,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8@3.254.0: @@ -4048,7 +4077,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-waiter@3.224.0: @@ -4057,7 +4086,7 @@ packages: dependencies: '@aws-sdk/abort-controller': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-waiter@3.257.0: @@ -4066,14 +4095,14 @@ packages: dependencies: '@aws-sdk/abort-controller': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/xml-builder@3.201.0: resolution: {integrity: sha512-brRdB1wwMgjWEnOQsv7zSUhIQuh7DEicrfslAqHop4S4FtSI3GQAShpQqgOpMTNFYcpaWKmE/Y1MJmNY7xLCnw==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@babel/code-frame@7.22.10: @@ -4084,11 +4113,25 @@ packages: chalk: 2.4.2 dev: true + /@babel/code-frame@7.26.2: + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + dev: true + /@babel/compat-data@7.22.9: resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} engines: {node: '>=6.9.0'} dev: true + /@babel/compat-data@7.26.8: + resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/core@7.18.6: resolution: {integrity: sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==} engines: {node: '>=6.9.0'} @@ -4112,6 +4155,29 @@ packages: - supports-color dev: true + /@babel/core@7.26.9: + resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9) + '@babel/helpers': 7.26.9 + '@babel/parser': 7.26.9 + '@babel/template': 7.26.9 + '@babel/traverse': 7.26.9 + '@babel/types': 7.26.9 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/generator@7.22.10: resolution: {integrity: sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==} engines: {node: '>=6.9.0'} @@ -4122,6 +4188,17 @@ packages: jsesc: 2.5.2 dev: true + /@babel/generator@7.26.9: + resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + dev: true + /@babel/helper-compilation-targets@7.22.10: resolution: {integrity: sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==} engines: {node: '>=6.9.0'} @@ -4133,6 +4210,17 @@ packages: semver: 6.3.1 dev: true + /@babel/helper-compilation-targets@7.26.5: + resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.26.8 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.4 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + /@babel/helper-environment-visitor@7.22.5: resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} engines: {node: '>=6.9.0'} @@ -4160,6 +4248,16 @@ packages: '@babel/types': 7.22.10 dev: true + /@babel/helper-module-imports@7.25.9: + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/traverse': 7.26.9 + '@babel/types': 7.26.9 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-module-transforms@7.22.9(@babel/core@7.18.6): resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} engines: {node: '>=6.9.0'} @@ -4174,6 +4272,20 @@ packages: '@babel/helper-validator-identifier': 7.22.5 dev: true + /@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9): + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.26.9 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.9 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-plugin-utils@7.22.5: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} engines: {node: '>=6.9.0'} @@ -4198,16 +4310,31 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-string-parser@7.25.9: + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-identifier@7.22.5: resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-identifier@7.25.9: + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-option@7.22.5: resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-option@7.25.9: + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helpers@7.22.10: resolution: {integrity: sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==} engines: {node: '>=6.9.0'} @@ -4219,6 +4346,14 @@ packages: - supports-color dev: true + /@babel/helpers@7.26.9: + resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.26.9 + '@babel/types': 7.26.9 + dev: true + /@babel/highlight@7.22.10: resolution: {integrity: sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==} engines: {node: '>=6.9.0'} @@ -4236,6 +4371,14 @@ packages: '@babel/types': 7.22.10 dev: true + /@babel/parser@7.26.9: + resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.26.9 + dev: true + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.18.6): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -4371,6 +4514,15 @@ packages: '@babel/types': 7.22.10 dev: true + /@babel/template@7.26.9: + resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 + dev: true + /@babel/traverse@7.22.10: resolution: {integrity: sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==} engines: {node: '>=6.9.0'} @@ -4389,6 +4541,21 @@ packages: - supports-color dev: true + /@babel/traverse@7.26.9: + resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.9 + '@babel/parser': 7.26.9 + '@babel/template': 7.26.9 + '@babel/types': 7.26.9 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types@7.22.10: resolution: {integrity: sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==} engines: {node: '>=6.9.0'} @@ -4398,6 +4565,14 @@ packages: to-fast-properties: 2.0.0 dev: true + /@babel/types@7.26.9: + resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + dev: true + /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true @@ -4957,6 +5132,438 @@ packages: dev: false optional: true + /@esbuild/aix-ppc64@0.21.5: + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/aix-ppc64@0.25.0: + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.21.5: + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.25.0: + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.21.5: + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.25.0: + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.21.5: + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.25.0: + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.21.5: + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.25.0: + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.21.5: + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.25.0: + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.21.5: + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.25.0: + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.21.5: + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.25.0: + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.21.5: + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.25.0: + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.21.5: + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.25.0: + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.21.5: + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.25.0: + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.21.5: + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.25.0: + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.21.5: + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.25.0: + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.21.5: + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.25.0: + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.21.5: + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.25.0: + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.21.5: + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.25.0: + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.21.5: + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.25.0: + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-arm64@0.25.0: + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.21.5: + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.25.0: + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-arm64@0.25.0: + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.21.5: + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.25.0: + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.21.5: + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.25.0: + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.21.5: + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.25.0: + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.21.5: + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.25.0: + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.21.5: + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.25.0: + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5073,6 +5680,22 @@ packages: resolution: {integrity: sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==} dev: false + /@hcaptcha/loader@1.2.4: + resolution: {integrity: sha512-3MNrIy/nWBfyVVvMPBKdKrX7BeadgiimW0AL/a/8TohNtJqxoySKgTJEXOQvYwlHemQpUzFrIsK74ody7JiMYw==} + dev: true + + /@hcaptcha/react-hcaptcha@1.11.2(react-dom@19.0.0)(react@19.0.0): + resolution: {integrity: sha512-F6+aZknrpTHoAhKIP80wWNJI5nhOJg0qyazM3pAw0bCyvVLSsveWeZyTK7W8EhO6nvovTE061gehlC+bmS/vMw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + dependencies: + '@babel/runtime': 7.22.10 + '@hcaptcha/loader': 1.2.4 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + dev: true + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -5474,6 +6097,13 @@ packages: '@sinclair/typebox': 0.24.51 dev: true + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jest/source-map@28.1.2: resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -5549,6 +6179,18 @@ packages: chalk: 4.1.2 dev: true + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.19.67 + '@types/yargs': 17.0.24 + chalk: 4.1.2 + dev: true + /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -5582,13 +6224,6 @@ packages: engines: {node: '>=6.0.0'} dev: true - /@jridgewell/source-map@0.3.6: - resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - dev: true - /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true @@ -5618,6 +6253,174 @@ packages: resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} dev: false + /@lingui/babel-plugin-extract-messages@5.2.0: + resolution: {integrity: sha512-hQ6tFK72ZXX2813PU9thJbnwJ+SjSrfR3/tt4aqHJcOUdrb67wMVY/0xiUe+vb5y6kVZjZ4oPqdgCfGZ2jWBEw==} + engines: {node: '>=20.0.0'} + dev: true + + /@lingui/babel-plugin-lingui-macro@5.2.0(babel-plugin-macros@3.1.0)(typescript@5.6.3): + resolution: {integrity: sha512-IEpEfKW2WoGiK30dbovwXaPj69dKUP+GEAk00/6KUMB0sonaBWO4NO3Bj9G6NSdA6fB1lm9BtvuPqJQ2DvjF5g==} + engines: {node: '>=20.0.0'} + peerDependencies: + babel-plugin-macros: 2 || 3 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dependencies: + '@babel/core': 7.26.9 + '@babel/runtime': 7.22.10 + '@babel/types': 7.26.9 + '@lingui/conf': 5.2.0(typescript@5.6.3) + '@lingui/core': 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + '@lingui/message-utils': 5.2.0 + babel-plugin-macros: 3.1.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@lingui/cli@5.2.0(typescript@5.6.3): + resolution: {integrity: sha512-SLMPi9VMNAmhKRGt3HCGIZVHHmxfAcb7zNK9qwrEhlvcwxNmtsPtLb4iJvKy/VpdCQYm7C6D34tFjyVjUZ4ROg==} + engines: {node: '>=20.0.0'} + hasBin: true + dependencies: + '@babel/core': 7.26.9 + '@babel/generator': 7.26.9 + '@babel/parser': 7.26.9 + '@babel/runtime': 7.22.10 + '@babel/types': 7.26.9 + '@lingui/babel-plugin-extract-messages': 5.2.0 + '@lingui/babel-plugin-lingui-macro': 5.2.0(babel-plugin-macros@3.1.0)(typescript@5.6.3) + '@lingui/conf': 5.2.0(typescript@5.6.3) + '@lingui/core': 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + '@lingui/format-po': 5.2.0(typescript@5.6.3) + '@lingui/message-utils': 5.2.0 + babel-plugin-macros: 3.1.0 + chalk: 4.1.2 + chokidar: 3.5.1 + cli-table: 0.3.11 + commander: 10.0.1 + convert-source-map: 2.0.0 + date-fns: 3.6.0 + esbuild: 0.21.5 + glob: 11.0.1 + inquirer: 7.3.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + ora: 5.4.1 + pathe: 1.1.2 + pkg-up: 3.1.0 + pofile: 1.1.4 + pseudolocale: 2.1.0 + source-map: 0.8.0-beta.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@lingui/conf@5.2.0(typescript@5.6.3): + resolution: {integrity: sha512-3biQJxGntCP+EnOe9jjlquGCBfk6ogq+I8ZduHwmBceY5aQ0OR7V23ItDrMz0NBy8dFNk5YoeHun3CYKYOS/Jg==} + engines: {node: '>=20.0.0'} + dependencies: + '@babel/runtime': 7.22.10 + chalk: 4.1.2 + cosmiconfig: 8.3.6(typescript@5.6.3) + jest-validate: 29.7.0 + jiti: 1.21.0 + lodash.get: 4.4.2 + transitivePeerDependencies: + - typescript + dev: true + + /@lingui/core@5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0): + resolution: {integrity: sha512-cz35uKDxIGb/CPvgwn7BM/QYpxtARmQm7n+mHUoNJdNKSrg9R7vKkLRG7k9dukZwix2Mdjh+2dPIJnAkor2CiA==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@lingui/babel-plugin-lingui-macro': 5.2.0 + babel-plugin-macros: 2 || 3 + peerDependenciesMeta: + '@lingui/babel-plugin-lingui-macro': + optional: true + babel-plugin-macros: + optional: true + dependencies: + '@babel/runtime': 7.22.10 + '@lingui/babel-plugin-lingui-macro': 5.2.0(babel-plugin-macros@3.1.0)(typescript@5.6.3) + '@lingui/message-utils': 5.2.0 + babel-plugin-macros: 3.1.0 + unraw: 3.0.0 + dev: true + + /@lingui/format-po@5.2.0(typescript@5.6.3): + resolution: {integrity: sha512-viUQaoRa8UxSghayTY7xjtwXbfXIVdlM8C4HsxmozQnl5TXnPVEwlaPYds3sdJ8PmQGcYCm35r8EsmuKBoWYDQ==} + engines: {node: '>=20.0.0'} + dependencies: + '@lingui/conf': 5.2.0(typescript@5.6.3) + '@lingui/message-utils': 5.2.0 + date-fns: 3.6.0 + pofile: 1.1.4 + transitivePeerDependencies: + - typescript + dev: true + + /@lingui/message-utils@5.2.0: + resolution: {integrity: sha512-qJFKNc1b7SRX6y5ywtA1x+2/gaY22e09hjC6fiDvDpAFdEguI4qAJGmBmqlAZG/kcokR0tmMpo9zYUF8jjcHEA==} + engines: {node: '>=20.0.0'} + dependencies: + '@messageformat/parser': 5.1.1 + js-sha256: 0.10.1 + dev: true + bundledDependencies: + - '@messageformat/date-skeleton' + + /@lingui/react@5.2.0(react@19.0.0): + resolution: {integrity: sha512-Ok9ZsA3hPPzeTXpp1woFk0Bgqv6shMB8AzbSQCixudLdbuAPkBu/EKlwTGBs0yYlq5cUXeDYrKKHF1R5BcX5HA==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@lingui/babel-plugin-lingui-macro': 5.2.0 + babel-plugin-macros: 2 || 3 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@lingui/babel-plugin-lingui-macro': + optional: true + babel-plugin-macros: + optional: true + dependencies: + '@babel/runtime': 7.22.10 + '@lingui/core': 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + react: 19.0.0 + dev: true + + /@lingui/swc-plugin@5.4.0(@lingui/core@5.2.0)(@swc/core@1.10.18): + resolution: {integrity: sha512-xn3na6sA+wQPhw6oxIZvDvL51XkLZtyCMSdrUZDMf5/j18JJI8QRewlfn13Ai+Ze8qw8tCbCKnry7a6Dlq38yw==} + peerDependencies: + '@lingui/core': '5' + '@swc/core': '*' + next: '*' + peerDependenciesMeta: + '@swc/core': + optional: true + next: + optional: true + dependencies: + '@lingui/core': 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + '@swc/core': 1.10.18(@swc/helpers@0.5.15) + dev: true + + /@lingui/vite-plugin@5.2.0(typescript@5.6.3)(vite@6.2.0): + resolution: {integrity: sha512-jMpf6JJY1s3t4eFRBseTyuQNxy6ERRwg+uLi8EZ/qcaQgQW+GK6qWX/Qg5xQ8k1mJpaP6ihanMQMrkS6d5oR/A==} + engines: {node: '>=20.0.0'} + peerDependencies: + vite: ^3 || ^4 || ^5.0.9 || ^6 + dependencies: + '@lingui/cli': 5.2.0(typescript@5.6.3) + '@lingui/conf': 5.2.0(typescript@5.6.3) + vite: 6.2.0(@types/node@18.19.67) + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: @@ -5638,6 +6441,12 @@ packages: read-yaml-file: 1.1.0 dev: true + /@messageformat/parser@5.1.1: + resolution: {integrity: sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==} + dependencies: + moo: 0.5.2 + dev: true + /@noble/curves@1.8.0: resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} engines: {node: ^14.21.3 || >=16} @@ -5839,8 +6648,27 @@ packages: rollup: 4.18.0 dev: true - /@rollup/plugin-dynamic-import-vars@2.1.2(rollup@4.18.0): - resolution: {integrity: sha512-4lr2oXxs9hcxtGGaK8s0i9evfjzDrAs7ngw28TqruWKTEm0+U4Eljb+F6HXGYdFv8xRojQlrQwV7M/yxeh3yzQ==} + /@rollup/plugin-commonjs@28.0.2(rollup@4.18.0): + resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.4.3(picomatch@4.0.2) + is-reference: 1.2.1 + magic-string: 0.30.10 + picomatch: 4.0.2 + rollup: 4.18.0 + dev: true + + /@rollup/plugin-dynamic-import-vars@2.1.5(rollup@4.18.0): + resolution: {integrity: sha512-Mymi24fd9hlRifdZV/jYIFj1dn99F34imiYu3KzlAcgBcRi3i9SucgW/VRo5SQ9K4NuQ7dCep6pFWgNyhRdFHQ==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -5851,7 +6679,7 @@ packages: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) astring: 1.8.6 estree-walker: 2.0.2 - fast-glob: 3.3.1 + fast-glob: 3.3.2 magic-string: 0.30.10 rollup: 4.18.0 dev: true @@ -5899,52 +6727,53 @@ packages: rollup: 4.18.0 dev: true - /@rollup/plugin-replace@5.0.5(rollup@4.18.0): - resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==} + /@rollup/plugin-node-resolve@16.0.0(rollup@4.18.0): + resolution: {integrity: sha512-0FPvAeVUT/zdWoO0jnb/V5BlBsUSNfkIOtFHzMO4H9MOklrmQFY6FduVHKucNb/aTFxvnGhj4MNj/T1oNdDfNg==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + rollup: ^2.78.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) - magic-string: 0.30.10 - rollup: 4.18.0 - dev: true - - /@rollup/plugin-terser@0.4.4(rollup@4.18.0): - resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - dependencies: - rollup: 4.18.0 - serialize-javascript: 6.0.2 - smob: 1.5.0 - terser: 5.31.0 - dev: true - - /@rollup/plugin-typescript@11.1.6(rollup@4.18.0)(typescript@5.6.3): - resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.14.0||^3.0.0||^4.0.0 - tslib: '*' - typescript: '>=3.7.0' - peerDependenciesMeta: - rollup: - optional: true - tslib: - optional: true - dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 resolve: 1.22.4 rollup: 4.18.0 - typescript: 5.6.3 + dev: true + + /@rollup/plugin-swc@0.4.0(@swc/core@1.10.18)(rollup@4.18.0): + resolution: {integrity: sha512-oAtqXa8rOl7BOK1Rz3rRxI+LIL53S9SqO2KSq2UUUzWgOgXg6492Jh5mL2mv/f9cpit8zFWdwILuVeozZ0C8mg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@swc/core': ^1.3.0 + rollup: ^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@swc/core': 1.10.18(@swc/helpers@0.5.15) + rollup: 4.18.0 + smob: 1.5.0 + dev: true + + /@rollup/plugin-swc@0.4.0(@swc/core@1.11.4)(rollup@4.18.0): + resolution: {integrity: sha512-oAtqXa8rOl7BOK1Rz3rRxI+LIL53S9SqO2KSq2UUUzWgOgXg6492Jh5mL2mv/f9cpit8zFWdwILuVeozZ0C8mg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@swc/core': ^1.3.0 + rollup: ^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@swc/core': 1.11.4(@swc/helpers@0.5.15) + rollup: 4.18.0 + smob: 1.5.0 dev: true /@rollup/pluginutils@5.1.0(rollup@4.18.0): @@ -5970,6 +6799,14 @@ packages: dev: true optional: true + /@rollup/rollup-android-arm-eabi@4.34.9: + resolution: {integrity: sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-android-arm64@4.18.0: resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} cpu: [arm64] @@ -5978,6 +6815,14 @@ packages: dev: true optional: true + /@rollup/rollup-android-arm64@4.34.9: + resolution: {integrity: sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-darwin-arm64@4.18.0: resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} cpu: [arm64] @@ -5986,6 +6831,14 @@ packages: dev: true optional: true + /@rollup/rollup-darwin-arm64@4.34.9: + resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-darwin-x64@4.18.0: resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} cpu: [x64] @@ -5994,6 +6847,30 @@ packages: dev: true optional: true + /@rollup/rollup-darwin-x64@4.34.9: + resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-freebsd-arm64@4.34.9: + resolution: {integrity: sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-freebsd-x64@4.34.9: + resolution: {integrity: sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm-gnueabihf@4.18.0: resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} cpu: [arm] @@ -6002,6 +6879,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-arm-gnueabihf@4.34.9: + resolution: {integrity: sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm-musleabihf@4.18.0: resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} cpu: [arm] @@ -6010,6 +6895,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-arm-musleabihf@4.34.9: + resolution: {integrity: sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm64-gnu@4.18.0: resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} cpu: [arm64] @@ -6018,6 +6911,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-arm64-gnu@4.34.9: + resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm64-musl@4.18.0: resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} cpu: [arm64] @@ -6026,6 +6927,22 @@ packages: dev: true optional: true + /@rollup/rollup-linux-arm64-musl@4.34.9: + resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-loongarch64-gnu@4.34.9: + resolution: {integrity: sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-powerpc64le-gnu@4.18.0: resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} cpu: [ppc64] @@ -6034,6 +6951,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-powerpc64le-gnu@4.34.9: + resolution: {integrity: sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-riscv64-gnu@4.18.0: resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} cpu: [riscv64] @@ -6042,6 +6967,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-riscv64-gnu@4.34.9: + resolution: {integrity: sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-s390x-gnu@4.18.0: resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} cpu: [s390x] @@ -6050,6 +6983,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-s390x-gnu@4.34.9: + resolution: {integrity: sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-x64-gnu@4.18.0: resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} cpu: [x64] @@ -6058,6 +6999,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-x64-gnu@4.34.9: + resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-x64-musl@4.18.0: resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} cpu: [x64] @@ -6066,6 +7015,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-x64-musl@4.34.9: + resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-arm64-msvc@4.18.0: resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} cpu: [arm64] @@ -6074,6 +7031,14 @@ packages: dev: true optional: true + /@rollup/rollup-win32-arm64-msvc@4.34.9: + resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-ia32-msvc@4.18.0: resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} cpu: [ia32] @@ -6082,6 +7047,14 @@ packages: dev: true optional: true + /@rollup/rollup-win32-ia32-msvc@4.34.9: + resolution: {integrity: sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-x64-msvc@4.18.0: resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} cpu: [x64] @@ -6090,6 +7063,14 @@ packages: dev: true optional: true + /@rollup/rollup-win32-x64-msvc@4.34.9: + resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rtsao/scc@1.1.0: resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} dev: true @@ -6098,6 +7079,10 @@ packages: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + /@sinonjs/commons@1.8.6: resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} dependencies: @@ -6125,6 +7110,24 @@ packages: tslib: 2.8.0 dev: false + /@swc/core-darwin-arm64@1.10.18: + resolution: {integrity: sha512-FdGqzAIKVQJu8ROlnHElP59XAUsUzCFSNsou+tY/9ba+lhu8R9v0OI5wXiPErrKGZpQFMmx/BPqqhx3X4SuGNg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-arm64@1.11.4: + resolution: {integrity: sha512-Oi4lt4wqjpp80pcCh+vzvpsESJ8XXozYCE5EM/dDpr+9m2oRpkseds7Gq4ulzgdbUDPo1jJ1PonjjrKpfKY+sQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@swc/core-darwin-arm64@1.3.42: resolution: {integrity: sha512-hM6RrZFyoCM9mX3cj/zM5oXwhAqjUdOCLXJx7KTQps7NIkv/Qjvobgvyf2gAb89j3ARNo9NdIoLjTjJ6oALtiA==} engines: {node: '>=10'} @@ -6134,6 +7137,24 @@ packages: dev: true optional: true + /@swc/core-darwin-x64@1.10.18: + resolution: {integrity: sha512-RZ73gZRituL/ZVLgrW6BYnQ5g8tuStG4cLUiPGJsUZpUm0ullSH6lHFvZTCBNFTfpQChG6eEhi2IdG6DwFp1lw==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.11.4: + resolution: {integrity: sha512-Tb7ez94DXxhX5iJ5slnAlT2gwJinQk3pMnQ46Npi6adKr3ZXM5Bdk0jpRUp8XjEcgNXkQRV1DtrySgCz6YlEnQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@swc/core-darwin-x64@1.3.42: resolution: {integrity: sha512-bjsWtHMb6wJK1+RGlBs2USvgZ0txlMk11y0qBLKo32gLKTqzUwRw0Fmfzuf6Ue2a/w//7eqMlPFEre4LvJajGw==} engines: {node: '>=10'} @@ -6143,6 +7164,24 @@ packages: dev: true optional: true + /@swc/core-linux-arm-gnueabihf@1.10.18: + resolution: {integrity: sha512-8iJqI3EkxJuuq21UHoen1VS+QlS23RvynRuk95K+Q2HBjygetztCGGEc+Xelx9a0uPkDaaAtFvds4JMDqb9SAA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.11.4: + resolution: {integrity: sha512-p1uV+6Mi+0M+1kL7qL206ZaohomYMW7yroXSLDTJXbIylx7wG2xrUQL6AFtz2DwqDoX/E8jMNBjp+GcEy8r8Ig==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-arm-gnueabihf@1.3.42: resolution: {integrity: sha512-Oe0ggMz3MyqXNfeVmY+bBTL0hFSNY3bx8dhcqsh4vXk/ZVGse94QoC4dd92LuPHmKT0x6nsUzB86x2jU9QHW5g==} engines: {node: '>=10'} @@ -6152,6 +7191,24 @@ packages: dev: true optional: true + /@swc/core-linux-arm64-gnu@1.10.18: + resolution: {integrity: sha512-8f1kSktWzMB6PG+r8lOlCfXz5E8Qhsmfwonn77T/OfjvGwQaWrcoASh2cdjpk3dydbf8jsKGPQE1lSc7GyjXRQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.11.4: + resolution: {integrity: sha512-4ijX4bWf9oc7kWkT6xUhugVGzEJ7U9c7CHNmt/xhI/yWsQdfM11+HECqWh7ay3m+aaEoVdvTeU5gykeF5jSxDA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-arm64-gnu@1.3.42: resolution: {integrity: sha512-ZJsa8NIW1RLmmHGTJCbM7OPSbBZ9rOMrLqDtUOGrT0uoJXZnnQqolflamB5wviW0X6h3Z3/PSTNGNDCJ3u3Lqg==} engines: {node: '>=10'} @@ -6161,6 +7218,24 @@ packages: dev: true optional: true + /@swc/core-linux-arm64-musl@1.10.18: + resolution: {integrity: sha512-4rv+E4VLdgQw6zjbTAauCAEExxChvxMpBUMCiZweTNPKbJJ2dY6BX2WGJ1ea8+RcgqR/Xysj3AFbOz1LBz6dGA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.11.4: + resolution: {integrity: sha512-XI+gOgcuSanejbAC5QXKTjNA3GUJi7bzHmeJbNhKpX9d349RdVwan0k9okHmhMBY7BywAg3LK0ovF9PmOLgMHg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-arm64-musl@1.3.42: resolution: {integrity: sha512-YpZwlFAfOp5vkm/uVUJX1O7N3yJDO1fDQRWqsOPPNyIJkI2ydlRQtgN6ZylC159Qv+TimfXnGTlNr7o3iBAqjg==} engines: {node: '>=10'} @@ -6170,6 +7245,24 @@ packages: dev: true optional: true + /@swc/core-linux-x64-gnu@1.10.18: + resolution: {integrity: sha512-vTNmyRBVP+sZca+vtwygYPGTNudTU6Gl6XhaZZ7cEUTBr8xvSTgEmYXoK/2uzyXpaTUI4Bmtp1x81cGN0mMoLQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.11.4: + resolution: {integrity: sha512-wyD6noaCPFayKOvl9mTxuiQoEULAagGuO0od2VkW7h4HvlgpOAZNekZYX73WEP/b+WuePNHurZ9KGpom43IzmA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-x64-gnu@1.3.42: resolution: {integrity: sha512-0ccpKnsZbyHBzaQFdP8U9i29nvOfKitm6oJfdJzlqsY/jCqwvD8kv2CAKSK8WhJz//ExI2LqNrDI0yazx5j7+A==} engines: {node: '>=10'} @@ -6179,6 +7272,24 @@ packages: dev: true optional: true + /@swc/core-linux-x64-musl@1.10.18: + resolution: {integrity: sha512-1TZPReKhFCeX776XaT6wegknfg+g3zODve+r4oslFHI+g7cInfWlxoGNDS3niPKyuafgCdOjme2g3OF+zzxfsQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.11.4: + resolution: {integrity: sha512-e2vG9gUF1BRX0BWqSEHop6u14l5BtV3VS2Pmr+oquc0Ycs/zj81xhYc3ML4ByK5OxDkAaKBWryAOKTLaJA/DVg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-x64-musl@1.3.42: resolution: {integrity: sha512-7eckRRuTZ6+3K21uyfXXgc2ZCg0mSWRRNwNT3wap2bYkKPeqTgb8pm8xYSZNEiMuDonHEat6XCCV36lFY6kOdQ==} engines: {node: '>=10'} @@ -6188,6 +7299,24 @@ packages: dev: true optional: true + /@swc/core-win32-arm64-msvc@1.10.18: + resolution: {integrity: sha512-o/2CsaWSN3bkzVQ6DA+BiFKSVEYvhWGA1h+wnL2zWmIDs2Knag54sOEXZkCaf8YQyZesGeXJtPEy9hh/vjJgkA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.11.4: + resolution: {integrity: sha512-rm51iljNqjCA/41gxYameuyjX1ENaTlvdxmaoPPYeUDt6hfypG93IxMJJCewaeHN9XfNxqZU7d4cupNqk+8nng==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@swc/core-win32-arm64-msvc@1.3.42: resolution: {integrity: sha512-t27dJkdw0GWANdN4TV0lY/V5vTYSx5SRjyzzZolep358ueCGuN1XFf1R0JcCbd1ojosnkQg2L7A7991UjXingg==} engines: {node: '>=10'} @@ -6197,6 +7326,24 @@ packages: dev: true optional: true + /@swc/core-win32-ia32-msvc@1.10.18: + resolution: {integrity: sha512-eTPASeJtk4mJDfWiYEiOC6OYUi/N7meHbNHcU8e+aKABonhXrIo/FmnTE8vsUtC6+jakT1TQBdiQ8fzJ1kJVwA==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.11.4: + resolution: {integrity: sha512-PHy3N6zlyU8te7Umi0ggXNbcx2VUkwpE59PW9FQQy9MBZM1Qn+OEGnO/4KLWjGFABw+9CwIeaRYgq6uCi1ry6A==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@swc/core-win32-ia32-msvc@1.3.42: resolution: {integrity: sha512-xfpc/Zt/aMILX4IX0e3loZaFyrae37u3MJCv1gJxgqrpeLi7efIQr3AmERkTK3mxTO6R5urSliWw2W3FyZ7D3Q==} engines: {node: '>=10'} @@ -6206,6 +7353,24 @@ packages: dev: true optional: true + /@swc/core-win32-x64-msvc@1.10.18: + resolution: {integrity: sha512-1Dud8CDBnc34wkBOboFBQud9YlV1bcIQtKSg7zC8LtwR3h+XAaCayZPkpGmmAlCv1DLQPvkF+s0JcaVC9mfffQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.11.4: + resolution: {integrity: sha512-0TiriDGl7Dr4ObfMBk07PS4Ql5hgQH0QnU3E8I+fbs45hqfwC5OrN47HOsXx4ZbEw8XYxp2NM8SGnVoTIm4J8w==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@swc/core-win32-x64-msvc@1.3.42: resolution: {integrity: sha512-ra2K4Tu++EJLPhzZ6L8hWUsk94TdK/2UKhL9dzCBhtzKUixsGCEqhtqH1zISXNvW8qaVLFIMUP37ULe80/IJaA==} engines: {node: '>=10'} @@ -6215,6 +7380,58 @@ packages: dev: true optional: true + /@swc/core@1.10.18(@swc/helpers@0.5.15): + resolution: {integrity: sha512-IUWKD6uQYGRy8w2X9EZrtYg1O3SCijlHbCXzMaHQYc1X7yjijQh4H3IVL9ssZZyVp2ZDfQZu4bD5DWxxvpyjvg==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + '@swc/types': 0.1.19 + optionalDependencies: + '@swc/core-darwin-arm64': 1.10.18 + '@swc/core-darwin-x64': 1.10.18 + '@swc/core-linux-arm-gnueabihf': 1.10.18 + '@swc/core-linux-arm64-gnu': 1.10.18 + '@swc/core-linux-arm64-musl': 1.10.18 + '@swc/core-linux-x64-gnu': 1.10.18 + '@swc/core-linux-x64-musl': 1.10.18 + '@swc/core-win32-arm64-msvc': 1.10.18 + '@swc/core-win32-ia32-msvc': 1.10.18 + '@swc/core-win32-x64-msvc': 1.10.18 + dev: true + + /@swc/core@1.11.4(@swc/helpers@0.5.15): + resolution: {integrity: sha512-EHl6eNod/914xDRK4nu7gr78riK2cfi4DkAMvJt6COdaNGOnbR5eKrLe3SnRizyzzrPcxUMhflDL5hrcXS8rAQ==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + '@swc/types': 0.1.19 + optionalDependencies: + '@swc/core-darwin-arm64': 1.11.4 + '@swc/core-darwin-x64': 1.11.4 + '@swc/core-linux-arm-gnueabihf': 1.11.4 + '@swc/core-linux-arm64-gnu': 1.11.4 + '@swc/core-linux-arm64-musl': 1.11.4 + '@swc/core-linux-x64-gnu': 1.11.4 + '@swc/core-linux-x64-musl': 1.11.4 + '@swc/core-win32-arm64-msvc': 1.11.4 + '@swc/core-win32-ia32-msvc': 1.11.4 + '@swc/core-win32-x64-msvc': 1.11.4 + dev: true + /@swc/core@1.3.42: resolution: {integrity: sha512-nVFUd5+7tGniM2cT3LXaqnu3735Cu4az8A9gAKK+8sdpASI52SWuqfDBmjFCK9xG90MiVDVp2PTZr0BWqCIzpw==} engines: {node: '>=10'} @@ -6232,6 +7449,27 @@ packages: '@swc/core-win32-x64-msvc': 1.3.42 dev: true + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: true + + /@swc/helpers@0.5.15: + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + dependencies: + tslib: 2.8.0 + dev: true + + /@swc/jest@0.2.24(@swc/core@1.11.4): + resolution: {integrity: sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==} + engines: {npm: '>= 7.0.0'} + peerDependencies: + '@swc/core': '*' + dependencies: + '@jest/create-cache-key-function': 27.5.1 + '@swc/core': 1.11.4(@swc/helpers@0.5.15) + jsonc-parser: 3.2.0 + dev: true + /@swc/jest@0.2.24(@swc/core@1.3.42): resolution: {integrity: sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==} engines: {npm: '>= 7.0.0'} @@ -6243,6 +7481,12 @@ packages: jsonc-parser: 3.2.0 dev: true + /@swc/types@0.1.19: + resolution: {integrity: sha512-WkAZaAfj44kh/UFdAQcrMP1I0nwRqpt27u+08LMBYMqmQfwwMofYoMh/48NGkMMRfC4ynpfwRbJuu8ErfNloeA==} + dependencies: + '@swc/counter': 0.1.3 + dev: true + /@tokenizer/token@0.3.0: resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} @@ -6347,6 +7591,10 @@ packages: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true + /@types/estree@1.0.6: + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + dev: true + /@types/express-serve-static-core@4.17.36: resolution: {integrity: sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==} dependencies: @@ -6458,6 +7706,10 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/parse-json@4.0.2: + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + dev: true + /@types/pg@8.6.6: resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} dependencies: @@ -6470,10 +7722,6 @@ packages: resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} dev: true - /@types/prop-types@15.7.12: - resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - dev: true - /@types/psl@1.1.3: resolution: {integrity: sha512-Iu174JHfLd7i/XkXY6VDrqSlPvTDQOtQI7wNAXKKOAADJ9TduRLkNdMgjGiMxSttUIZnomv81JAbAbC0DhggxA==} dev: true @@ -6484,16 +7732,17 @@ packages: /@types/range-parser@1.2.4: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} - /@types/react-dom@18.3.0: - resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + /@types/react-dom@19.0.4(@types/react@19.0.10): + resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} + peerDependencies: + '@types/react': ^19.0.0 dependencies: - '@types/react': 18.3.2 + '@types/react': 19.0.10 dev: true - /@types/react@18.3.2: - resolution: {integrity: sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==} + /@types/react@19.0.10: + resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==} dependencies: - '@types/prop-types': 15.7.12 csstype: 3.1.3 dev: true @@ -6707,11 +7956,22 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true + /@vitejs/plugin-react-swc@3.8.0(@swc/helpers@0.5.15)(vite@6.2.0): + resolution: {integrity: sha512-T4sHPvS+DIqDP51ifPqa9XIRAz/kIvIi8oXcnOZZgHmMotgmmdxe/DD5tMFlt5nuIRzT0/QuiwmKlH0503Aapw==} + peerDependencies: + vite: ^4 || ^5 || ^6 + dependencies: + '@swc/core': 1.11.4(@swc/helpers@0.5.15) + vite: 6.2.0(@types/node@18.19.67) + transitivePeerDependencies: + - '@swc/helpers' + dev: true + /@web/rollup-plugin-import-meta-assets@2.2.1(rollup@4.18.0): resolution: {integrity: sha512-nG7nUQqSJWdl63pBTmnIElJuFi2V1x9eVje19BJuFvfz266jSmZtX3m30ncb7fOJxQt3/ge+FVL8tuNI9+63dQ==} engines: {node: '>=18.0.0'} dependencies: - '@rollup/plugin-dynamic-import-vars': 2.1.2(rollup@4.18.0) + '@rollup/plugin-dynamic-import-vars': 2.1.5(rollup@4.18.0) '@rollup/pluginutils': 5.1.0(rollup@4.18.0) estree-walker: 2.0.2 globby: 13.2.2 @@ -7027,7 +8287,7 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: true /astring@1.8.6: @@ -7050,7 +8310,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.23.0 - caniuse-lite: 1.0.30001621 + caniuse-lite: 1.0.30001700 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.1 @@ -7129,6 +8389,15 @@ packages: '@types/babel__traverse': 7.20.1 dev: true + /babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + dependencies: + '@babel/runtime': 7.22.10 + cosmiconfig: 7.1.0 + resolve: 1.22.4 + dev: true + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.18.6): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: @@ -7308,6 +8577,13 @@ packages: dependencies: fill-range: 7.0.1 + /braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.1.1 + dev: true + /breakword@1.0.6: resolution: {integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==} dependencies: @@ -7322,7 +8598,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001522 + caniuse-lite: 1.0.30001700 electron-to-chromium: 1.4.499 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.10) @@ -7333,12 +8609,23 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001621 + caniuse-lite: 1.0.30001700 electron-to-chromium: 1.4.779 node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.0) dev: true + /browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001700 + electron-to-chromium: 1.5.107 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.24.4) + dev: true + /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: @@ -7476,17 +8763,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.21.10 - caniuse-lite: 1.0.30001522 + caniuse-lite: 1.0.30001700 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true - /caniuse-lite@1.0.30001522: - resolution: {integrity: sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==} - dev: true - - /caniuse-lite@1.0.30001621: - resolution: {integrity: sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==} + /caniuse-lite@1.0.30001700: + resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==} dev: true /cbor-extract@2.2.0: @@ -7540,6 +8823,21 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true + /chokidar@3.5.1: + resolution: {integrity: sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.5.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -7587,6 +8885,30 @@ packages: engines: {node: '>=6'} dev: true + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + dev: true + + /cli-table@0.3.11: + resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} + engines: {node: '>= 0.2.0'} + dependencies: + colors: 1.0.3 + dev: true + + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: true + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -7675,14 +8997,20 @@ packages: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true + /colors@1.0.3: + resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} + engines: {node: '>=0.1.90'} + dev: true + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} dev: true /commander@4.1.1: @@ -7752,6 +9080,10 @@ packages: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + /cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -7771,6 +9103,33 @@ packages: object-assign: 4.1.1 vary: 1.1.2 + /cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + + /cosmiconfig@8.3.6(typescript@5.6.3): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.6.3 + dev: true + /cosmiconfig@9.0.0(typescript@5.6.3): resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -7977,6 +9336,10 @@ packages: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} dev: true + /date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + dev: true + /dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} dev: true @@ -8337,6 +9700,10 @@ packages: resolution: {integrity: sha512-oaTiIcszNfySXVJzKcjxd2YjPxziAd+GmXyb2HbidCeFo6Z88ygOT7EimlrEQhM2U08VhSrbKhLOXP0kKUCZ6g==} dev: true + /electron-to-chromium@1.5.107: + resolution: {integrity: sha512-dJr1o6yCntRkXElnhsHh1bAV19bo/hKyFf7tCcWgpXbuFIF0Lakjgqv5LRfSDaNzAII8Fnxg2tqgHkgCvxdbxw==} + dev: true + /elliptic@6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} dependencies: @@ -8777,10 +10144,79 @@ packages: esbuild-windows-arm64: 0.14.48 dev: true + /esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + dev: true + + /esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + dev: true + /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} + /escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + dev: true + /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -9260,7 +10696,7 @@ packages: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.8 dev: true /fast-json-stable-stringify@2.1.0: @@ -9322,6 +10758,24 @@ packages: pend: 1.2.0 dev: true + /fdir@6.4.3(picomatch@4.0.2): + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dependencies: + picomatch: 4.0.2 + dev: true + + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -9346,6 +10800,13 @@ packages: dependencies: to-regex-range: 5.0.1 + /fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + /finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} engines: {node: '>= 0.8'} @@ -9360,6 +10821,13 @@ packages: transitivePeerDependencies: - supports-color + /find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -9651,6 +11119,19 @@ packages: minipass: 5.0.0 path-scurry: 1.10.1 + /glob@11.0.1: + resolution: {integrity: sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==} + engines: {node: 20 || >=22} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 4.1.0 + minimatch: 10.0.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -9711,7 +11192,7 @@ packages: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.1 + fast-glob: 3.3.2 ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 @@ -10092,6 +11573,25 @@ packages: /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + /inquirer@7.3.3: + resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} + engines: {node: '>=8.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + run-async: 2.4.1 + rxjs: 6.6.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + dev: true + /int64-buffer@0.1.10: resolution: {integrity: sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA==} dev: false @@ -10290,6 +11790,11 @@ packages: dependencies: is-extglob: 2.1.1 + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + /is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} dev: true @@ -10406,6 +11911,11 @@ packages: which-typed-array: 1.1.16 dev: true + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + /is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -10498,6 +12008,13 @@ packages: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + /jackspeak@4.1.0: + resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} + engines: {node: 20 || >=22} + dependencies: + '@isaacs/cliui': 8.0.2 + dev: true + /jest-changed-files@28.1.3: resolution: {integrity: sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -10653,6 +12170,11 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dev: true + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /jest-haste-map@28.1.3: resolution: {integrity: sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -10869,6 +12391,18 @@ packages: pretty-format: 28.1.3 dev: true + /jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + dev: true + /jest-watcher@28.1.3: resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -10934,6 +12468,10 @@ packages: engines: {node: '>=10'} dev: true + /js-sha256@0.10.1: + resolution: {integrity: sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==} + dev: true + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true @@ -10963,6 +12501,12 @@ packages: hasBin: true dev: true + /jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + dev: true + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -11128,6 +12672,14 @@ packages: engines: {node: '>= 12.13.0'} dev: true + /locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -11148,6 +12700,11 @@ packages: /lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + dev: true + /lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} dev: true @@ -11197,7 +12754,6 @@ packages: /lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - dev: false /lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -11206,21 +12762,31 @@ packages: /lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + /long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} dev: false - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - dev: true - /lru-cache@10.2.0: resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} engines: {node: 14 || >=16.14} + /lru-cache@11.0.2: + resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} + engines: {node: 20 || >=22} + dev: true + /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: @@ -11349,6 +12915,14 @@ packages: braces: 3.0.2 picomatch: 2.3.1 + /micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + dev: true + /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -11396,6 +12970,13 @@ packages: /minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + /minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -11483,6 +13064,11 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} + /minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + /minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} @@ -11512,6 +13098,10 @@ packages: resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} dev: false + /moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + dev: true + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -11538,6 +13128,10 @@ packages: resolution: {integrity: sha512-5vQEh3y+DG/lMPM0mCGPDnyV8chYg/g7rl6v3Gd8WMF9S429ox3Xk8qrk174kWhG767KQMqqxLD1WnGd77hiew==} dev: false + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -11552,6 +13146,12 @@ packages: hasBin: true dev: true + /nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} @@ -11646,6 +13246,10 @@ packages: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} dev: true + /node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + dev: true + /nodemailer-html-to-text@3.2.0: resolution: {integrity: sha512-RJUC6640QV1PzTHHapOrc6IzrAJUZtk2BdVdINZ9VTLm+mcQNyBO9LYyhrnufkzqiD9l8hPLJ97rSyK4WanPNg==} engines: {node: '>= 10.23.0'} @@ -11844,6 +13448,21 @@ packages: type-check: 0.4.0 dev: true + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -11877,6 +13496,13 @@ packages: dependencies: yocto-queue: 0.1.0 + /p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -11951,6 +13577,10 @@ packages: netmask: 2.0.2 dev: true + /package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + dev: true + /packet-reader@1.0.0: resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} @@ -11979,6 +13609,11 @@ packages: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} dev: false + /path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -12003,6 +13638,14 @@ packages: lru-cache: 10.2.0 minipass: 5.0.0 + /path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + dependencies: + lru-cache: 11.0.2 + minipass: 7.1.2 + dev: true + /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -12011,6 +13654,10 @@ packages: engines: {node: '>=8'} dev: true + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true + /peek-readable@4.1.0: resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} engines: {node: '>=8'} @@ -12076,10 +13723,19 @@ packages: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} dev: true + /picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + dev: true + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + dev: true + /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -12166,6 +13822,17 @@ packages: find-up: 4.1.0 dev: true + /pkg-up@3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + dependencies: + find-up: 3.0.0 + dev: true + + /pofile@1.1.4: + resolution: {integrity: sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g==} + dev: true + /possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -12597,6 +14264,15 @@ packages: source-map-js: 1.2.0 dev: true + /postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + dev: true + /postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -12688,6 +14364,15 @@ packages: react-is: 18.2.0 dev: true + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + /process-warning@3.0.0: resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} @@ -12775,6 +14460,14 @@ packages: /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + /pseudolocale@2.1.0: + resolution: {integrity: sha512-af5fsrRvVwD+MBasBJvuDChT0KDqT0nEwD9NTgbtHJ16FKomWac9ua0z6YVNB4G9x9IOaiGWym62aby6n4tFMA==} + engines: {node: '>=16.0.0'} + hasBin: true + dependencies: + commander: 10.0.1 + dev: true + /pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true @@ -12853,12 +14546,6 @@ packages: engines: {node: '>=8'} dev: true - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - dependencies: - safe-buffer: 5.2.1 - dev: true - /range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -12884,25 +14571,31 @@ packages: minimist: 1.2.8 strip-json-comments: 2.0.1 - /react-dom@18.3.1(react@18.3.1): - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + /react-dom@19.0.0(react@19.0.0): + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} peerDependencies: - react: ^18.3.1 + react: ^19.0.0 dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react: 19.0.0 + scheduler: 0.25.0 + dev: true + + /react-error-boundary@5.0.0(react@19.0.0): + resolution: {integrity: sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==} + peerDependencies: + react: '>=16.13.1' + dependencies: + '@babel/runtime': 7.22.10 + react: 19.0.0 dev: true /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true - /react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + /react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} - dependencies: - loose-envify: 1.4.0 dev: true /read-cache@1.0.0: @@ -12964,6 +14657,13 @@ packages: dependencies: readable-stream: 3.6.2 + /readdirp@3.5.0: + resolution: {integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -13091,6 +14791,14 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + /retry@0.10.1: resolution: {integrity: sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==} dev: false @@ -13194,16 +14902,57 @@ packages: fsevents: 2.3.3 dev: true + /rollup@4.34.9: + resolution: {integrity: sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.9 + '@rollup/rollup-android-arm64': 4.34.9 + '@rollup/rollup-darwin-arm64': 4.34.9 + '@rollup/rollup-darwin-x64': 4.34.9 + '@rollup/rollup-freebsd-arm64': 4.34.9 + '@rollup/rollup-freebsd-x64': 4.34.9 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.9 + '@rollup/rollup-linux-arm-musleabihf': 4.34.9 + '@rollup/rollup-linux-arm64-gnu': 4.34.9 + '@rollup/rollup-linux-arm64-musl': 4.34.9 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.9 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.9 + '@rollup/rollup-linux-riscv64-gnu': 4.34.9 + '@rollup/rollup-linux-s390x-gnu': 4.34.9 + '@rollup/rollup-linux-x64-gnu': 4.34.9 + '@rollup/rollup-linux-x64-musl': 4.34.9 + '@rollup/rollup-win32-arm64-msvc': 4.34.9 + '@rollup/rollup-win32-ia32-msvc': 4.34.9 + '@rollup/rollup-win32-x64-msvc': 4.34.9 + fsevents: 2.3.3 + dev: true + + /run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 + /rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + dependencies: + tslib: 1.14.1 + dev: true + /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} requiresBuild: true dependencies: - tslib: 2.6.2 + tslib: 2.8.0 optional: true /safe-array-concat@1.0.1: @@ -13261,10 +15010,8 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} requiresBuild: true - /scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - dependencies: - loose-envify: 1.4.0 + /scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} dev: true /scmp@2.1.0: @@ -13327,12 +15074,6 @@ packages: transitivePeerDependencies: - supports-color - /serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - dependencies: - randombytes: 2.1.0 - dev: true - /serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -13564,15 +15305,13 @@ packages: engines: {node: '>=0.10.0'} dev: true - /source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 + /source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} dev: true - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 @@ -13587,6 +15326,13 @@ packages: engines: {node: '>= 8'} dev: false + /source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + dependencies: + whatwg-url: 7.1.0 + dev: true + /spawndamnit@2.0.0: resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} dependencies: @@ -13923,7 +15669,7 @@ packages: engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@pkgr/core': 0.1.1 - tslib: 2.6.2 + tslib: 2.8.0 dev: true /tailwindcss@3.4.3: @@ -14023,17 +15769,6 @@ packages: supports-hyperlinks: 2.3.0 dev: true - /terser@5.31.0: - resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==} - engines: {node: '>=10'} - hasBin: true - dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.10.0 - commander: 2.20.3 - source-map-support: 0.5.21 - dev: true - /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -14120,6 +15855,12 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + /tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + dependencies: + punycode: 2.3.0 + dev: true + /trim-newlines@3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} @@ -14188,15 +15929,14 @@ packages: /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: false /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} requiresBuild: true + dev: false /tslib@2.8.0: resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} - dev: false /tty-table@4.2.1: resolution: {integrity: sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==} @@ -14460,6 +16200,10 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + /unraw@3.0.0: + resolution: {integrity: sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==} + dev: true + /update-browserslist-db@1.0.11(browserslist@4.21.10): resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} hasBin: true @@ -14482,6 +16226,17 @@ packages: picocolors: 1.0.1 dev: true + /update-browserslist-db@1.1.3(browserslist@4.24.4): + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 + dev: true + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -14538,6 +16293,54 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + /vite@6.2.0(@types/node@18.19.67): + resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + dependencies: + '@types/node': 18.19.67 + esbuild: 0.25.0 + postcss: 8.5.3 + rollup: 4.34.9 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: @@ -14553,12 +16356,24 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + /webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 + /whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + dev: true + /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: diff --git a/tsconfig/bundler.json b/tsconfig/bundler.json index 6aea841fa..d0480c35c 100644 --- a/tsconfig/bundler.json +++ b/tsconfig/bundler.json @@ -3,6 +3,7 @@ "compilerOptions": { "module": "ESNext", "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, "declaration": false, "declarationMap": false, "noEmit": true