Commit 6a437a33 by CaIon

feat(oauth): add OAuth callback URL display and copy functionality

parent 57865fc1
......@@ -50,9 +50,13 @@ export function PresetSelector(props: PresetSelectorProps) {
// Auto-fill name, slug, icon, and field mappings immediately
props.form.setValue('name', preset.name, { shouldDirty: true })
props.form.setValue('slug', presetKey.toLowerCase().replace(/\s+/g, '-'), {
props.form.setValue(
'slug',
presetKey.toLowerCase().replaceAll(/\s+/g, '-'),
{
shouldDirty: true,
})
}
)
props.form.setValue('icon', preset.icon, { shouldDirty: true })
props.form.setValue('scopes', preset.scopes, { shouldDirty: true })
props.form.setValue('user_id_field', preset.user_id_field, {
......@@ -111,12 +115,10 @@ export function PresetSelector(props: PresetSelectorProps) {
<div className='space-y-1.5'>
<Label>{t('Preset Template')}</Label>
<Select
items={[
...OAUTH_PRESETS.map((preset) => ({
items={OAUTH_PRESETS.map((preset) => ({
value: preset.key,
label: preset.name,
})),
]}
}))}
value={selectedPreset}
onValueChange={(v) => v !== null && handlePresetChange(v)}
>
......
......@@ -18,10 +18,12 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { zodResolver } from '@hookform/resolvers/zod'
import { useEffect } from 'react'
import { type Resolver, useForm } from 'react-hook-form'
import { type Resolver, useForm, useWatch } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { CopyButton } from '@/components/copy-button'
import { Dialog } from '@/components/dialog'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import {
Form,
......@@ -50,6 +52,7 @@ import {
SettingsSwitchContent,
SettingsSwitchItem,
} from '../../../components/settings-form-layout'
import { buildOAuthCallbackUrl } from '../../oauth-callback-url'
import {
useCreateProvider,
useUpdateProvider,
......@@ -67,6 +70,7 @@ type ProviderFormDialogProps = {
open: boolean
onOpenChange: (open: boolean) => void
provider?: CustomOAuthProvider | null
serverAddress: string
}
const PROVIDER_FORM_ID = 'custom-oauth-provider-form'
......@@ -102,6 +106,13 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
access_denied_message: '',
},
})
const watchedSlug = useWatch({ control: form.control, name: 'slug' })
const callbackPath = watchedSlug?.trim() || '{slug}'
const callbackUrl = buildOAuthCallbackUrl(
props.serverAddress,
callbackPath,
t('Site URL')
)
useEffect(() => {
if (props.open && props.provider) {
......@@ -169,6 +180,12 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
}
const isPending = createProvider.isPending || updateProvider.isPending
let submitLabel = t('Create Provider')
if (isPending) {
submitLabel = t('Saving...')
} else if (isEditing) {
submitLabel = t('Update Provider')
}
return (
<Dialog
......@@ -194,11 +211,7 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
{t('Cancel')}
</Button>
<Button type='submit' form={PROVIDER_FORM_ID} disabled={isPending}>
{isPending
? t('Saving...')
: isEditing
? t('Update Provider')
: t('Create Provider')}
{submitLabel}
</Button>
</>
}
......@@ -211,6 +224,34 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
{/* Preset Selector (only for creating) */}
{!isEditing && <PresetSelector form={form} />}
<Alert>
<AlertTitle>{t('OAuth callback URL')}</AlertTitle>
<AlertDescription className='space-y-3 text-sm'>
<p>
{t(
'This callback URL updates from the slug field and is the value to register with your provider.'
)}
</p>
<div className='flex min-w-0 flex-col gap-1.5 sm:flex-row sm:items-center sm:justify-between'>
<span className='text-muted-foreground shrink-0'>
{t('Authorization callback URL')}
</span>
<span className='flex min-w-0 items-center gap-2'>
<code className='bg-muted text-foreground min-w-0 rounded px-1.5 py-0.5 text-xs break-all'>
{callbackUrl}
</code>
<CopyButton
value={callbackUrl}
size='icon'
className='size-7'
tooltip={t('Copy callback URL')}
aria-label={t('Copy callback URL')}
/>
</span>
</div>
</AlertDescription>
</Alert>
{/* Basic Info */}
<div className='space-y-4'>
<h4 className='text-sm font-medium'>{t('Basic Info')}</h4>
......@@ -341,12 +382,10 @@ export function ProviderFormDialog(props: ProviderFormDialogProps) {
<FormItem>
<FormLabel>{t('Auth Style')}</FormLabel>
<Select
items={[
...AUTH_STYLE_OPTIONS.map((option) => ({
items={AUTH_STYLE_OPTIONS.map((option) => ({
value: String(option.value),
label: t(option.labelKey),
})),
]}
}))}
value={String(field.value)}
onValueChange={(val) => field.onChange(Number(val))}
>
......
......@@ -19,18 +19,31 @@ For commercial licensing, please contact support@quantumnous.com
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { CopyButton } from '@/components/copy-button'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { SettingsSection } from '../../components/settings-section'
import { buildOAuthCallbackUrl } from '../oauth-callback-url'
import { ProviderFormDialog } from './components/provider-form-dialog'
import { ProviderTable } from './components/provider-table'
import { useCustomOAuthProviders } from './hooks/use-custom-oauth-providers'
import type { CustomOAuthProvider } from './types'
export function CustomOAuthSection() {
type CustomOAuthSectionProps = {
serverAddress: string
}
export function CustomOAuthSection(props: CustomOAuthSectionProps) {
const { t } = useTranslation()
const { data: providers = [], isLoading } = useCustomOAuthProviders()
const [dialogOpen, setDialogOpen] = useState(false)
const [editingProvider, setEditingProvider] =
useState<CustomOAuthProvider | null>(null)
const callbackFormat = buildOAuthCallbackUrl(
props.serverAddress,
'{slug}',
t('Site URL')
)
const handleCreate = () => {
setEditingProvider(null)
......@@ -61,6 +74,34 @@ export function CustomOAuthSection() {
return (
<SettingsSection title={t('Custom OAuth Providers')}>
<Alert>
<AlertTitle>{t('Callback URL format')}</AlertTitle>
<AlertDescription className='space-y-3 text-sm'>
<p>
{t(
'Use this callback URL pattern when registering a custom OAuth provider.'
)}
</p>
<div className='flex min-w-0 flex-col gap-1.5 sm:flex-row sm:items-center sm:justify-between'>
<span className='text-muted-foreground shrink-0'>
{t('OAuth callback URL')}
</span>
<span className='flex min-w-0 items-center gap-2'>
<code className='bg-muted text-foreground min-w-0 rounded px-1.5 py-0.5 text-xs break-all'>
{callbackFormat}
</code>
<CopyButton
value={callbackFormat}
size='icon'
className='size-7'
tooltip={t('Copy callback URL')}
aria-label={t('Copy callback URL')}
/>
</span>
</div>
</AlertDescription>
</Alert>
<ProviderTable
providers={providers}
onEdit={handleEdit}
......@@ -71,6 +112,7 @@ export function CustomOAuthSection() {
open={dialogOpen}
onOpenChange={handleDialogChange}
provider={editingProvider}
serverAddress={props.serverAddress}
/>
</SettingsSection>
)
......
......@@ -32,6 +32,7 @@ const defaultAuthSettings: AuthSettings = {
EmailDomainRestrictionEnabled: false,
EmailAliasRestrictionEnabled: false,
EmailDomainWhitelist: '',
ServerAddress: '',
GitHubOAuthEnabled: false,
GitHubClientId: '',
GitHubClientSecret: '',
......
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
export function resolveOAuthSiteUrl(
serverAddress: string,
fallback: string
): string {
const normalized = serverAddress.trim().replace(/\/+$/, '')
return normalized || fallback
}
export function buildOAuthCallbackUrl(
serverAddress: string,
callbackPath: string,
fallback: string
): string {
const siteUrl = resolveOAuthSiteUrl(serverAddress, fallback)
return `${siteUrl}/oauth/${callbackPath.replace(/^\/+/, '')}`
}
......@@ -18,12 +18,15 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { zodResolver } from '@hookform/resolvers/zod'
import axios from 'axios'
import { useEffect, useMemo, useRef, useState } from 'react'
import { ExternalLink } from 'lucide-react'
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
import { useForm } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import * as z from 'zod'
import { CopyButton } from '@/components/copy-button'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import {
Form,
FormControl,
......@@ -47,6 +50,10 @@ import {
import { SettingsPageFormActions } from '../components/settings-page-context'
import { SettingsSection } from '../components/settings-section'
import { useUpdateOption } from '../hooks/use-update-option'
import {
buildOAuthCallbackUrl,
resolveOAuthSiteUrl,
} from './oauth-callback-url'
/**
* react-hook-form 7 treats dotted `name` strings as nested paths. To keep
......@@ -117,6 +124,55 @@ type FlatOAuthDefaults = {
const oauthTabContentClassName =
'grid min-w-0 gap-x-5 gap-y-6 lg:grid-cols-2 [&>[data-slot=form-item]]:min-w-0 lg:[&>[data-slot=form-item]:has([data-slot=switch])]:col-span-2'
type OAuthSetupGuideRow = {
label: ReactNode
value: string
copyLabel: string
}
type OAuthSetupGuideProps = {
title: string
description: ReactNode
rows: OAuthSetupGuideRow[]
children?: ReactNode
}
function OAuthSetupGuide(props: OAuthSetupGuideProps) {
return (
<Alert className='lg:col-span-2'>
<AlertTitle>{props.title}</AlertTitle>
<AlertDescription className='space-y-3 text-sm'>
<div>{props.description}</div>
<div className='space-y-2'>
{props.rows.map((row) => (
<div
key={`${String(row.label)}-${row.value}`}
className='flex min-w-0 flex-col gap-1.5 sm:flex-row sm:items-center sm:justify-between'
>
<span className='text-muted-foreground shrink-0'>
{row.label}
</span>
<span className='flex min-w-0 items-center gap-2'>
<code className='bg-muted text-foreground min-w-0 rounded px-1.5 py-0.5 text-xs break-all'>
{row.value}
</code>
<CopyButton
value={row.value}
size='icon'
className='size-7'
tooltip={row.copyLabel}
aria-label={row.copyLabel}
/>
</span>
</div>
))}
</div>
{props.children}
</AlertDescription>
</Alert>
)
}
const buildFormDefaults = (defaults: FlatOAuthDefaults): OAuthFormValues => ({
GitHubOAuthEnabled: defaults.GitHubOAuthEnabled,
GitHubClientId: defaults.GitHubClientId ?? '',
......@@ -177,12 +233,34 @@ const normalizeFormValues = (values: OAuthFormValues): FlatOAuthDefaults => ({
type OAuthSectionProps = {
defaultValues: FlatOAuthDefaults
serverAddress: string
}
export function OAuthSection(props: OAuthSectionProps) {
const { t } = useTranslation()
const updateOption = useUpdateOption()
const [activeTab, setActiveTab] = useState('github')
const siteUrl = resolveOAuthSiteUrl(props.serverAddress, t('Site URL'))
const githubCallbackUrl = buildOAuthCallbackUrl(
props.serverAddress,
'github',
t('Site URL')
)
const discordCallbackUrl = buildOAuthCallbackUrl(
props.serverAddress,
'discord',
t('Site URL')
)
const oidcCallbackUrl = buildOAuthCallbackUrl(
props.serverAddress,
'oidc',
t('Site URL')
)
const linuxDOCallbackUrl = buildOAuthCallbackUrl(
props.serverAddress,
'linuxdo',
t('Site URL')
)
const formDefaults = useMemo(
() => buildFormDefaults(props.defaultValues),
......@@ -306,6 +384,25 @@ export function OAuthSection(props: OAuthSectionProps) {
</TabsList>
<TabsContent value='github' className={oauthTabContentClassName}>
<OAuthSetupGuide
title={t('Setup guide')}
description={t(
'Set these values in the provider application before enabling login.'
)}
rows={[
{
label: t('Homepage URL'),
value: siteUrl,
copyLabel: t('Copy homepage URL'),
},
{
label: t('Authorization callback URL'),
value: githubCallbackUrl,
copyLabel: t('Copy callback URL'),
},
]}
/>
<FormField
control={form.control}
name='GitHubOAuthEnabled'
......@@ -378,6 +475,25 @@ export function OAuthSection(props: OAuthSectionProps) {
</TabsContent>
<TabsContent value='discord' className={oauthTabContentClassName}>
<OAuthSetupGuide
title={t('Setup guide')}
description={t(
'Set these values in the provider application before enabling login.'
)}
rows={[
{
label: t('Homepage URL'),
value: siteUrl,
copyLabel: t('Copy homepage URL'),
},
{
label: t('Authorization callback URL'),
value: discordCallbackUrl,
copyLabel: t('Copy callback URL'),
},
]}
/>
<FormField
control={form.control}
name='discord.enabled'
......@@ -450,6 +566,36 @@ export function OAuthSection(props: OAuthSectionProps) {
</TabsContent>
<TabsContent value='oidc' className={oauthTabContentClassName}>
<OAuthSetupGuide
title={t('Setup guide')}
description={
<div className='space-y-1'>
<p>
{t(
'Set these values in the provider application before enabling login.'
)}
</p>
<p>
{t(
'OIDC discovery can fill the endpoint fields automatically when the provider supports it.'
)}
</p>
</div>
}
rows={[
{
label: t('Homepage URL'),
value: siteUrl,
copyLabel: t('Copy homepage URL'),
},
{
label: t('Redirect URL'),
value: oidcCallbackUrl,
copyLabel: t('Copy redirect URL'),
},
]}
/>
<FormField
control={form.control}
name='oidc.enabled'
......@@ -702,6 +848,30 @@ export function OAuthSection(props: OAuthSectionProps) {
</TabsContent>
<TabsContent value='linuxdo' className={oauthTabContentClassName}>
<OAuthSetupGuide
title={t('Setup guide')}
description={t(
'Set these values in the provider application before enabling login.'
)}
rows={[
{
label: t('Authorization callback URL'),
value: linuxDOCallbackUrl,
copyLabel: t('Copy callback URL'),
},
]}
>
<a
href='https://connect.linux.do/'
target='_blank'
rel='noreferrer'
className='text-primary inline-flex w-fit items-center gap-1 underline underline-offset-3 hover:no-underline'
>
{t('Manage your LinuxDO OAuth app')}
<ExternalLink className='size-3' aria-hidden='true' />
</a>
</OAuthSetupGuide>
<FormField
control={form.control}
name='LinuxDOOAuthEnabled'
......
......@@ -47,6 +47,7 @@ const AUTH_SECTIONS = [
titleKey: 'OAuth Integrations',
build: (settings: AuthSettings) => (
<OAuthSection
serverAddress={settings.ServerAddress}
defaultValues={{
GitHubOAuthEnabled: settings.GitHubOAuthEnabled,
GitHubClientId: settings.GitHubClientId,
......@@ -115,7 +116,9 @@ const AUTH_SECTIONS = [
{
id: 'custom-oauth',
titleKey: 'Custom OAuth',
build: () => <CustomOAuthSection />,
build: (settings: AuthSettings) => (
<CustomOAuthSection serverAddress={settings.ServerAddress} />
),
},
] as const
......
......@@ -129,6 +129,7 @@ export type AuthSettings = {
EmailDomainRestrictionEnabled: boolean
EmailAliasRestrictionEnabled: boolean
EmailDomainWhitelist: string
ServerAddress: string
GitHubOAuthEnabled: boolean
GitHubClientId: string
GitHubClientSecret: string
......
......@@ -491,6 +491,7 @@
"Authentication": "Authentication",
"Authentication Method": "Authentication Method",
"Authenticator code": "Authenticator code",
"Authorization callback URL": "Authorization callback URL",
"Authorization Endpoint": "Authorization Endpoint",
"Authorization Endpoint (Optional)": "Authorization Endpoint (Optional)",
"Authorize": "Authorize",
......@@ -706,6 +707,7 @@
"Callback Caller IP": "Callback Caller IP",
"Callback notification URL": "Callback notification URL",
"Callback Payment Method": "Callback Payment Method",
"Callback URL format": "Callback URL format",
"Cancel": "Cancel",
"Cancelled": "Cancelled",
"Cancelled at": "Cancelled at",
......@@ -1064,12 +1066,14 @@
"Copy all backup codes": "Copy all backup codes",
"Copy All Codes": "Copy All Codes",
"Copy API key": "Copy API key",
"Copy callback URL": "Copy callback URL",
"Copy Channel": "Copy Channel",
"Copy code": "Copy code",
"Copy Connection Info": "Copy Connection Info",
"Copy failed": "Copy failed",
"Copy Field": "Copy Field",
"Copy Header": "Copy Header",
"Copy homepage URL": "Copy homepage URL",
"Copy Key": "Copy Key",
"Copy Link": "Copy Link",
"Copy model name": "Copy model name",
......@@ -1077,6 +1081,7 @@
"Copy prompt": "Copy prompt",
"Copy ready-to-run curl": "Copy ready-to-run curl",
"Copy redemption code": "Copy redemption code",
"Copy redirect URL": "Copy redirect URL",
"Copy referral link": "Copy referral link",
"Copy Request Header": "Copy Request Header",
"Copy secret key": "Copy secret key",
......@@ -2179,6 +2184,7 @@
"Hit tier": "Hit tier",
"Home": "Home",
"Home Page Content": "Home Page Content",
"Homepage URL": "Homepage URL",
"Hostname or IP of your SMTP provider": "Hostname or IP of your SMTP provider",
"Hour": "Hour",
"Hour of day": "Hour of day",
......@@ -2511,6 +2517,7 @@
"Manage subscription plans and pricing.": "Manage subscription plans and pricing.",
"Manage Subscriptions": "Manage Subscriptions",
"Manage Vendors": "Manage Vendors",
"Manage your LinuxDO OAuth app": "Manage your LinuxDO OAuth app",
"Manage your security settings and account access": "Manage your security settings and account access",
"Manual Disabled": "Manual Disabled",
"Map fields from the user info response to local user attributes. Supports nested paths (e.g. ocs.data.id).": "Map fields from the user info response to local user attributes. Supports nested paths (e.g. ocs.data.id).",
......@@ -2981,6 +2988,7 @@
"Number of tokens per unit quota": "Number of tokens per unit quota",
"Number of top log probabilities returned per token": "Number of top log probabilities returned per token",
"Number of users invited": "Number of users invited",
"OAuth callback URL": "OAuth callback URL",
"OAuth Client ID": "OAuth Client ID",
"OAuth Client Secret": "OAuth Client Secret",
"OAuth failed": "OAuth failed",
......@@ -3009,6 +3017,7 @@
"OIDC Client ID": "OIDC Client ID",
"OIDC Client Secret": "OIDC Client Secret",
"OIDC configuration fetched successfully": "OIDC configuration fetched successfully",
"OIDC discovery can fill the endpoint fields automatically when the provider supports it.": "OIDC discovery can fill the endpoint fields automatically when the provider supports it.",
"OIDC discovery URL. Click \"Auto-discover\" to fetch endpoints automatically.": "OIDC discovery URL. Click \"Auto-discover\" to fetch endpoints automatically.",
"OIDC endpoints discovered successfully": "OIDC endpoints discovered successfully",
"Old Format Template": "Old Format Template",
......@@ -3598,6 +3607,7 @@
"redemption codes.": "redemption codes.",
"Redemption failed": "Redemption failed",
"Redemption successful! Added: {{quota}}": "Redemption successful! Added: {{quota}}",
"Redirect URL": "Redirect URL",
"Redirecting to chat page...": "Redirecting to chat page...",
"Redirecting to Creem checkout...": "Redirecting to Creem checkout...",
"Redirecting to GitHub...": "Redirecting to GitHub...",
......@@ -4062,6 +4072,7 @@
"Set tag for selected channels": "Set tag for selected channels",
"Set the language used across the interface": "Set the language used across the interface",
"Set the user's role (cannot be Root)": "Set the user's role (cannot be Root)",
"Set these values in the provider application before enabling login.": "Set these values in the provider application before enabling login.",
"Setting Key": "Setting Key",
"Setting saved": "Setting saved",
"Setting up 2FA...": "Setting up 2FA...",
......@@ -4120,6 +4131,7 @@
"Single Key": "Single Key",
"Site & Branding": "Site & Branding",
"Site Key": "Site Key",
"Site URL": "Site URL",
"Size:": "Size:",
"sk_xxx or rk_xxx": "sk_xxx or rk_xxx",
"Skip async task polling delay": "Skip async task polling delay",
......@@ -4428,6 +4440,7 @@
"This action will permanently remove 2FA protection from your account.": "This action will permanently remove 2FA protection from your account.",
"This announcement will be removed from the list.": "This announcement will be removed from the list.",
"This API shortcut will be removed from the list.": "This API shortcut will be removed from the list.",
"This callback URL updates from the slug field and is the value to register with your provider.": "This callback URL updates from the slug field and is the value to register with your provider.",
"This channel has no configured models.": "This channel has no configured models.",
"This channel is not an Ollama channel.": "This channel is not an Ollama channel.",
"This channel type does not support fetching models": "This channel type does not support fetching models",
......@@ -4799,6 +4812,7 @@
"Use the full-width table to scan prices, then select a row to edit it here.": "Use the full-width table to scan prices, then select a row to edit it here.",
"Use the group set on the token. If the token has no group, use the user group. The auto group tries the auto assignment order from top to bottom.": "Use the group set on the token. If the token has no group, use the user group. The auto group tries the auto assignment order from top to bottom.",
"Use the pricing group table to manage the ratio and whether the group appears in the token creation dropdown.": "Use the pricing group table to manage the ratio and whether the group appears in the token creation dropdown.",
"Use this callback URL pattern when registering a custom OAuth provider.": "Use this callback URL pattern when registering a custom OAuth provider.",
"Use this token for API authentication": "Use this token for API authentication",
"Use your Passkey": "Use your Passkey",
"used": "used",
......
......@@ -491,6 +491,7 @@
"Authentication": "Authentification",
"Authentication Method": "Méthode d'authentification",
"Authenticator code": "Code d'authentification",
"Authorization callback URL": "URL de rappel d'autorisation",
"Authorization Endpoint": "Point de terminaison d'autorisation",
"Authorization Endpoint (Optional)": "Point de terminaison d'autorisation (Facultatif)",
"Authorize": "Autoriser",
......@@ -706,6 +707,7 @@
"Callback Caller IP": "IP de l’appelant du callback",
"Callback notification URL": "URL de notification de rappel",
"Callback Payment Method": "Moyen de paiement (callback)",
"Callback URL format": "Format de l'URL de rappel",
"Cancel": "Annuler",
"Cancelled": "Annulé",
"Cancelled at": "Annulé le",
......@@ -1064,12 +1066,14 @@
"Copy all backup codes": "Copier tous les codes de sauvegarde",
"Copy All Codes": "Copier tous les codes",
"Copy API key": "Copier la clé API",
"Copy callback URL": "Copier l'URL de rappel",
"Copy Channel": "Copier le canal",
"Copy code": "Copier le code",
"Copy Connection Info": "Copier les infos de connexion",
"Copy failed": "Copie échouée",
"Copy Field": "Copier le champ",
"Copy Header": "Copier l'en-tête",
"Copy homepage URL": "Copier l'URL de la page d'accueil",
"Copy Key": "Copier la clé",
"Copy Link": "Copier le lien",
"Copy model name": "Copier le nom du modèle",
......@@ -1077,6 +1081,7 @@
"Copy prompt": "Copier le prompt",
"Copy ready-to-run curl": "Copier le curl prêt à exécuter",
"Copy redemption code": "Copier le code de rachat",
"Copy redirect URL": "Copier l'URL de redirection",
"Copy referral link": "Copier le lien de parrainage",
"Copy Request Header": "Copier un en-tête de requête",
"Copy secret key": "Copier la clé secrète",
......@@ -2179,6 +2184,7 @@
"Hit tier": "Palier atteint",
"Home": "Accueil",
"Home Page Content": "Contenu de la page d'accueil",
"Homepage URL": "URL de la page d'accueil",
"Hostname or IP of your SMTP provider": "Nom d'hôte ou IP de votre fournisseur SMTP",
"Hour": "Heure",
"Hour of day": "Heure du jour",
......@@ -2511,6 +2517,7 @@
"Manage subscription plans and pricing.": "Gérer les plans d'abonnement et les tarifs.",
"Manage Subscriptions": "Gérer les abonnements",
"Manage Vendors": "Gérer les fournisseurs",
"Manage your LinuxDO OAuth app": "Gérer votre application OAuth LinuxDO",
"Manage your security settings and account access": "Gérer vos paramètres de sécurité et l'accès à votre compte",
"Manual Disabled": "Désactivé manuellement",
"Map fields from the user info response to local user attributes. Supports nested paths (e.g. ocs.data.id).": "Mapper les champs de la réponse des informations utilisateur vers les attributs utilisateur locaux. Supporte les chemins imbriqués (par exemple ocs.data.id).",
......@@ -2981,6 +2988,7 @@
"Number of tokens per unit quota": "Nombre de jetons par unité de quota",
"Number of top log probabilities returned per token": "Nombre de log-probabilités retournées par jeton",
"Number of users invited": "Nombre d'utilisateurs invités",
"OAuth callback URL": "URL de rappel OAuth",
"OAuth Client ID": "ID client OAuth",
"OAuth Client Secret": "Secret client OAuth",
"OAuth failed": "Échec de l'OAuth",
......@@ -3009,6 +3017,7 @@
"OIDC Client ID": "ID Client OIDC",
"OIDC Client Secret": "Secret Client OIDC",
"OIDC configuration fetched successfully": "Configuration OIDC récupérée avec succès",
"OIDC discovery can fill the endpoint fields automatically when the provider supports it.": "La découverte OIDC peut remplir automatiquement les champs de point de terminaison lorsque le fournisseur la prend en charge.",
"OIDC discovery URL. Click \"Auto-discover\" to fetch endpoints automatically.": "URL de découverte OIDC. Cliquez sur \"Découverte automatique\" pour récupérer les points de terminaison automatiquement.",
"OIDC endpoints discovered successfully": "Points de terminaison OIDC découverts avec succès",
"Old Format Template": "Modèle Ancien Format",
......@@ -3598,6 +3607,7 @@
"redemption codes.": "codes de rachat.",
"Redemption failed": "Rédemption échouée",
"Redemption successful! Added: {{quota}}": "Échange réussi ! Ajouté : {{quota}}",
"Redirect URL": "URL de redirection",
"Redirecting to chat page...": "Redirection vers la page de discussion...",
"Redirecting to Creem checkout...": "Redirection vers la caisse Creem...",
"Redirecting to GitHub...": "Redirection vers GitHub...",
......@@ -4062,6 +4072,7 @@
"Set tag for selected channels": "Définir un tag pour les canaux sélectionnés",
"Set the language used across the interface": "Définir la langue utilisée dans l'interface",
"Set the user's role (cannot be Root)": "Définir le rôle de l'utilisateur (ne peut pas être Root)",
"Set these values in the provider application before enabling login.": "Renseignez ces valeurs dans l'application du fournisseur avant d'activer la connexion.",
"Setting Key": "Clé de paramètre",
"Setting saved": "Paramètre sauvegardé",
"Setting up 2FA...": "Configuration de la 2FA...",
......@@ -4120,6 +4131,7 @@
"Single Key": "Clé unique",
"Site & Branding": "Site et marque",
"Site Key": "Clé du site",
"Site URL": "URL du site",
"Size:": "Taille :",
"sk_xxx or rk_xxx": "sk_xxx ou rk_xxx",
"Skip async task polling delay": "Ignorer le délai de polling des tâches asynchrones",
......@@ -4428,6 +4440,7 @@
"This action will permanently remove 2FA protection from your account.": "Cette action supprimera définitivement la protection 2FA de votre compte.",
"This announcement will be removed from the list.": "Cette annonce sera retirée de la liste.",
"This API shortcut will be removed from the list.": "Ce raccourci API sera retiré de la liste.",
"This callback URL updates from the slug field and is the value to register with your provider.": "Cette URL de rappel se met à jour à partir du champ slug et doit être enregistrée auprès de votre fournisseur.",
"This channel has no configured models.": "Ce canal n'a aucun modèle configuré.",
"This channel is not an Ollama channel.": "Ce canal n'est pas un canal Ollama.",
"This channel type does not support fetching models": "Ce type de canal ne prend pas en charge la récupération de modèles",
......@@ -4799,6 +4812,7 @@
"Use the full-width table to scan prices, then select a row to edit it here.": "Parcourez les prix dans le tableau, puis sélectionnez une ligne pour la modifier ici.",
"Use the group set on the token. If the token has no group, use the user group. The auto group tries the auto assignment order from top to bottom.": "Utilisez le groupe défini sur le jeton. S’il n’en a pas, utilisez le groupe de l’utilisateur. Le groupe auto essaie l’ordre d’affectation automatique de haut en bas.",
"Use the pricing group table to manage the ratio and whether the group appears in the token creation dropdown.": "Utilisez le tableau des groupes tarifaires pour gérer le ratio et l’apparition du groupe dans la liste de création de jeton.",
"Use this callback URL pattern when registering a custom OAuth provider.": "Utilisez ce modèle d'URL de rappel lors de l'enregistrement d'un fournisseur OAuth personnalisé.",
"Use this token for API authentication": "Utilisez ce jeton pour l'authentification API",
"Use your Passkey": "Utiliser votre clé d'accès (Passkey)",
"used": "utilisé",
......
......@@ -491,6 +491,7 @@
"Authentication": "認証",
"Authentication Method": "認証方式",
"Authenticator code": "認証コード",
"Authorization callback URL": "認可コールバック URL",
"Authorization Endpoint": "認可エンドポイント",
"Authorization Endpoint (Optional)": "認証エンドポイント (オプション)",
"Authorize": "認証",
......@@ -706,6 +707,7 @@
"Callback Caller IP": "コールバック呼び出し元 IP",
"Callback notification URL": "コールバック通知URL",
"Callback Payment Method": "コールバック支払い方法",
"Callback URL format": "コールバック URL 形式",
"Cancel": "キャンセル",
"Cancelled": "キャンセル",
"Cancelled at": "キャンセル日時",
......@@ -1064,12 +1066,14 @@
"Copy all backup codes": "すべてのバックアップコードをコピー",
"Copy All Codes": "すべてのコードをコピー",
"Copy API key": "APIキーをコピー",
"Copy callback URL": "コールバック URL をコピー",
"Copy Channel": "チャネルをコピー",
"Copy code": "コードをコピー",
"Copy Connection Info": "接続情報をコピー",
"Copy failed": "コピーに失敗しました",
"Copy Field": "フィールドをコピー",
"Copy Header": "ヘッダーをコピー",
"Copy homepage URL": "ホームページ URL をコピー",
"Copy Key": "キーをコピー",
"Copy Link": "リンクをコピー",
"Copy model name": "モデル名をコピー",
......@@ -1077,6 +1081,7 @@
"Copy prompt": "プロンプトをコピー",
"Copy ready-to-run curl": "そのまま実行できる curl をコピー",
"Copy redemption code": "引き換えコードをコピー",
"Copy redirect URL": "リダイレクト URL をコピー",
"Copy referral link": "紹介リンクをコピー",
"Copy Request Header": "リクエストヘッダーをコピー",
"Copy secret key": "シークレットキーをコピー",
......@@ -2179,6 +2184,7 @@
"Hit tier": "一致したティア",
"Home": "ホーム",
"Home Page Content": "ホームコンテンツ",
"Homepage URL": "ホームページ URL",
"Hostname or IP of your SMTP provider": "SMTP プロバイダーのホスト名または IP",
"Hour": "時間",
"Hour of day": "時刻(時)",
......@@ -2511,6 +2517,7 @@
"Manage subscription plans and pricing.": "サブスクリプションプランと価格設定を管理します。",
"Manage Subscriptions": "サブスクリプションの管理",
"Manage Vendors": "ベンダーの管理",
"Manage your LinuxDO OAuth app": "LinuxDO OAuth アプリを管理",
"Manage your security settings and account access": "セキュリティ設定とアカウントアクセスを管理する",
"Manual Disabled": "手動無効",
"Map fields from the user info response to local user attributes. Supports nested paths (e.g. ocs.data.id).": "ユーザー情報レスポンスのフィールドをローカルユーザー属性にマッピングします。ネストされたパスをサポートします (例: ocs.data.id)。",
......@@ -2981,6 +2988,7 @@
"Number of tokens per unit quota": "単位クォータあたりのトークン数",
"Number of top log probabilities returned per token": "トークンごとに返される上位対数確率の数",
"Number of users invited": "招待されたユーザー数",
"OAuth callback URL": "OAuth コールバック URL",
"OAuth Client ID": "OAuthクライアントID",
"OAuth Client Secret": "OAuthクライアントシークレット",
"OAuth failed": "OAuth に失敗しました",
......@@ -3009,6 +3017,7 @@
"OIDC Client ID": "OIDCクライアントID",
"OIDC Client Secret": "OIDCクライアントシークレット",
"OIDC configuration fetched successfully": "OIDC 設定が正常に取得されました",
"OIDC discovery can fill the endpoint fields automatically when the provider supports it.": "プロバイダーが対応している場合、OIDC Discovery でエンドポイント項目を自動入力できます。",
"OIDC discovery URL. Click \"Auto-discover\" to fetch endpoints automatically.": "OIDCディスカバリーURL。「自動検出」をクリックすると、エンドポイントを自動的に取得します。",
"OIDC endpoints discovered successfully": "OIDCエンドポイントの検出に成功しました",
"Old Format Template": "旧形式テンプレート",
......@@ -3598,6 +3607,7 @@
"redemption codes.": "引き換えコード。",
"Redemption failed": "交換に失敗しました",
"Redemption successful! Added: {{quota}}": "引き換え成功!追加:{{quota}}",
"Redirect URL": "リダイレクト URL",
"Redirecting to chat page...": "チャットページにリダイレクト中...",
"Redirecting to Creem checkout...": "Creemチェックアウトにリダイレクト中...",
"Redirecting to GitHub...": "GitHub にリダイレクトしています...",
......@@ -4062,6 +4072,7 @@
"Set tag for selected channels": "選択したチャネルにタグを設定",
"Set the language used across the interface": "インターフェースで使用する言語を設定します",
"Set the user's role (cannot be Root)": "ユーザーのロールを設定します(Rootにはできません)",
"Set these values in the provider application before enabling login.": "ログインを有効にする前に、プロバイダーアプリでこれらの値を設定してください。",
"Setting Key": "設定キー",
"Setting saved": "設定が保存されました",
"Setting up 2FA...": "2FAを設定中...",
......@@ -4120,6 +4131,7 @@
"Single Key": "単一キー",
"Site & Branding": "サイトとブランド",
"Site Key": "サイトキー",
"Site URL": "サイト URL",
"Size:": "サイズ:",
"sk_xxx or rk_xxx": "sk_xxx または rk_xxx",
"Skip async task polling delay": "非同期タスクのポーリング遅延をスキップ",
......@@ -4428,6 +4440,7 @@
"This action will permanently remove 2FA protection from your account.": "この操作により、アカウントから2FA保護が完全に削除されます。",
"This announcement will be removed from the list.": "このお知らせはリストから削除されます。",
"This API shortcut will be removed from the list.": "この API ショートカットはリストから削除されます。",
"This callback URL updates from the slug field and is the value to register with your provider.": "このコールバック URL は slug フィールドに合わせて更新されます。プロバイダーに登録する値です。",
"This channel has no configured models.": "このチャンネルには構成されたモデルがありません。",
"This channel is not an Ollama channel.": "このチャネルはOllamaチャネルではありません。",
"This channel type does not support fetching models": "このチャネルタイプはモデルの取得をサポートしていません",
......@@ -4799,6 +4812,7 @@
"Use the full-width table to scan prices, then select a row to edit it here.": "表で価格を確認し、行を選択してここで編集します。",
"Use the group set on the token. If the token has no group, use the user group. The auto group tries the auto assignment order from top to bottom.": "トークンに設定されたグループを使います。トークンにグループがなければユーザーグループを使います。auto グループは自動割り当て順を上から順に試します。",
"Use the pricing group table to manage the ratio and whether the group appears in the token creation dropdown.": "料金グループ表で倍率と、トークン作成ドロップダウンに表示するかどうかを管理します。",
"Use this callback URL pattern when registering a custom OAuth provider.": "カスタム OAuth プロバイダーを登録するときは、このコールバック URL 形式を使用します。",
"Use this token for API authentication": "API認証にはこのトークンを使用してください",
"Use your Passkey": "パスキーを使用",
"used": "使用済み",
......
......@@ -491,6 +491,7 @@
"Authentication": "Аутентификация",
"Authentication Method": "Метод аутентификации",
"Authenticator code": "Код аутентификатора",
"Authorization callback URL": "URL обратного вызова авторизации",
"Authorization Endpoint": "Конечная точка авторизации",
"Authorization Endpoint (Optional)": "Конечная точка авторизации (необязательно)",
"Authorize": "Авторизовать",
......@@ -706,6 +707,7 @@
"Callback Caller IP": "IP вызывающей стороны callback",
"Callback notification URL": "URL обратного вызова",
"Callback Payment Method": "Способ оплаты (callback)",
"Callback URL format": "Формат URL обратного вызова",
"Cancel": "Отмена",
"Cancelled": "Отменено",
"Cancelled at": "Отменено",
......@@ -1064,12 +1066,14 @@
"Copy all backup codes": "Скопировать все резервные коды",
"Copy All Codes": "Скопировать все коды",
"Copy API key": "Скопировать ключ API",
"Copy callback URL": "Скопировать URL обратного вызова",
"Copy Channel": "Скопировать канал",
"Copy code": "Копировать код",
"Copy Connection Info": "Копировать данные подключения",
"Copy failed": "Не удалось скопировать",
"Copy Field": "Копировать поле",
"Copy Header": "Копировать заголовок",
"Copy homepage URL": "Скопировать URL главной страницы",
"Copy Key": "Копировать ключ",
"Copy Link": "Копировать ссылку",
"Copy model name": "Скопировать имя модели",
......@@ -1077,6 +1081,7 @@
"Copy prompt": "Копировать промпт",
"Copy ready-to-run curl": "Скопировать готовый curl",
"Copy redemption code": "Скопировать код активации",
"Copy redirect URL": "Скопировать URL перенаправления",
"Copy referral link": "Скопировать реферальную ссылку",
"Copy Request Header": "Копировать заголовок запроса",
"Copy secret key": "Скопировать секретный ключ",
......@@ -2179,6 +2184,7 @@
"Hit tier": "Совпавший уровень",
"Home": "Главная",
"Home Page Content": "Содержимое главной страницы",
"Homepage URL": "URL главной страницы",
"Hostname or IP of your SMTP provider": "Имя хоста или IP-адрес вашего SMTP-провайдера",
"Hour": "Час",
"Hour of day": "Час суток",
......@@ -2511,6 +2517,7 @@
"Manage subscription plans and pricing.": "Управление планами подписок и ценообразованием.",
"Manage Subscriptions": "Управление подписками",
"Manage Vendors": "Управление поставщиками",
"Manage your LinuxDO OAuth app": "Управлять приложением OAuth LinuxDO",
"Manage your security settings and account access": "Управление настройками безопасности и доступом к аккаунту",
"Manual Disabled": "Ручное отключение",
"Map fields from the user info response to local user attributes. Supports nested paths (e.g. ocs.data.id).": "Сопоставление полей из ответа информации о пользователе с локальными атрибутами пользователя. Поддерживает вложенные пути (например, ocs.data.id).",
......@@ -2981,6 +2988,7 @@
"Number of tokens per unit quota": "Количество токенов на единицу квоты",
"Number of top log probabilities returned per token": "Количество top-вероятностей на токен",
"Number of users invited": "Количество приглашенных пользователей",
"OAuth callback URL": "URL обратного вызова OAuth",
"OAuth Client ID": "Идентификатор клиента OAuth",
"OAuth Client Secret": "OAuth Client Secret",
"OAuth failed": "OAuth не удался",
......@@ -3009,6 +3017,7 @@
"OIDC Client ID": "ID клиента OIDC",
"OIDC Client Secret": "Секрет клиента OIDC",
"OIDC configuration fetched successfully": "Конфигурация OIDC успешно получена",
"OIDC discovery can fill the endpoint fields automatically when the provider supports it.": "OIDC Discovery может автоматически заполнить поля конечных точек, если провайдер это поддерживает.",
"OIDC discovery URL. Click \"Auto-discover\" to fetch endpoints automatically.": "URL обнаружения OIDC. Нажмите \"Автообнаружение\" для автоматического получения конечных точек.",
"OIDC endpoints discovered successfully": "Конечные точки OIDC успешно обнаружены",
"Old Format Template": "Шаблон старого формата",
......@@ -3598,6 +3607,7 @@
"redemption codes.": "коды активации.",
"Redemption failed": "Погашение не удалось",
"Redemption successful! Added: {{quota}}": "Погашение успешно! Добавлено: {{quota}}",
"Redirect URL": "URL перенаправления",
"Redirecting to chat page...": "Перенаправление на страницу чата...",
"Redirecting to Creem checkout...": "Перенаправление на кассу Creem...",
"Redirecting to GitHub...": "Перенаправление на GitHub...",
......@@ -4062,6 +4072,7 @@
"Set tag for selected channels": "Установить тег для выбранных каналов",
"Set the language used across the interface": "Настроить язык интерфейса",
"Set the user's role (cannot be Root)": "Установить роль пользователя (не может быть Root)",
"Set these values in the provider application before enabling login.": "Укажите эти значения в приложении провайдера перед включением входа.",
"Setting Key": "Ключ настройки",
"Setting saved": "Настройка сохранена",
"Setting up 2FA...": "Настройка 2FA...",
......@@ -4120,6 +4131,7 @@
"Single Key": "Одиночный ключ",
"Site & Branding": "Сайт и брендинг",
"Site Key": "Ключ сайта",
"Site URL": "URL сайта",
"Size:": "Размер:",
"sk_xxx or rk_xxx": "sk_xxx или rk_xxx",
"Skip async task polling delay": "Пропускать задержку опроса асинхронных задач",
......@@ -4428,6 +4440,7 @@
"This action will permanently remove 2FA protection from your account.": "Это действие безвозвратно удалит защиту 2FA из вашей учетной записи.",
"This announcement will be removed from the list.": "Это объявление будет удалено из списка.",
"This API shortcut will be removed from the list.": "Этот ярлык API будет удален из списка.",
"This callback URL updates from the slug field and is the value to register with your provider.": "Этот URL обратного вызова обновляется на основе поля slug; зарегистрируйте его у провайдера.",
"This channel has no configured models.": "У этого канала нет настроенных моделей.",
"This channel is not an Ollama channel.": "Этот канал не является каналом Ollama.",
"This channel type does not support fetching models": "Этот тип канала не поддерживает получение моделей",
......@@ -4799,6 +4812,7 @@
"Use the full-width table to scan prices, then select a row to edit it here.": "Просмотрите цены в таблице, затем выберите строку для редактирования здесь.",
"Use the group set on the token. If the token has no group, use the user group. The auto group tries the auto assignment order from top to bottom.": "Используется группа токена. Если у токена нет группы — группа пользователя. Группа auto перебирает порядок автоназначения сверху вниз.",
"Use the pricing group table to manage the ratio and whether the group appears in the token creation dropdown.": "Используйте таблицу групп тарификации, чтобы управлять коэффициентом и отображением группы в списке создания токена.",
"Use this callback URL pattern when registering a custom OAuth provider.": "Используйте этот формат URL обратного вызова при регистрации пользовательского провайдера OAuth.",
"Use this token for API authentication": "Используйте этот токен для аутентификации API",
"Use your Passkey": "Используйте свой ключ доступа",
"used": "использовано",
......
......@@ -491,6 +491,7 @@
"Authentication": "Xác thực",
"Authentication Method": "Phương thức xác thực",
"Authenticator code": "Mã xác thực",
"Authorization callback URL": "URL callback ủy quyền",
"Authorization Endpoint": "Điểm cuối ủy quyền",
"Authorization Endpoint (Optional)": "Điểm cuối ủy quyền (Tùy chọn)",
"Authorize": "Ủy quyền",
......@@ -706,6 +707,7 @@
"Callback Caller IP": "IP người gọi callback",
"Callback notification URL": "URL thông báo callback",
"Callback Payment Method": "Phương thức thanh toán callback",
"Callback URL format": "Định dạng URL callback",
"Cancel": "Hủy bỏ",
"Cancelled": "Đã hủy",
"Cancelled at": "Đã hủy lúc",
......@@ -1064,12 +1066,14 @@
"Copy all backup codes": "Sao chép tất cả mã dự phòng",
"Copy All Codes": "Sao chép Tất cả Mã",
"Copy API key": "Sao chép khóa API",
"Copy callback URL": "Sao chép URL callback",
"Copy Channel": "Sao chép kênh",
"Copy code": "Sao chép mã",
"Copy Connection Info": "Sao chép thông tin kết nối",
"Copy failed": "Sao chép thất bại",
"Copy Field": "Sao chép trường",
"Copy Header": "Sao chép tiêu đề",
"Copy homepage URL": "Sao chép URL trang chủ",
"Copy Key": "Sao chép khóa",
"Copy Link": "Sao chép liên kết",
"Copy model name": "Sao chép tên mô hình",
......@@ -1077,6 +1081,7 @@
"Copy prompt": "Sao chép prompt",
"Copy ready-to-run curl": "Sao chép curl có thể chạy ngay",
"Copy redemption code": "Sao chép mã đổi thưởng",
"Copy redirect URL": "Sao chép URL chuyển hướng",
"Copy referral link": "Sao chép liên kết giới thiệu",
"Copy Request Header": "Sao chép header yêu cầu",
"Copy secret key": "Sao chép khóa bí mật",
......@@ -2179,6 +2184,7 @@
"Hit tier": "Bậc trúng",
"Home": "Trang chủ",
"Home Page Content": "Nội dung Trang chủ",
"Homepage URL": "URL trang chủ",
"Hostname or IP of your SMTP provider": "Tên máy chủ hoặc IP của nhà cung cấp SMTP của bạn",
"Hour": "Giờ",
"Hour of day": "Giờ trong ngày",
......@@ -2511,6 +2517,7 @@
"Manage subscription plans and pricing.": "Quản lý gói đăng ký và giá cả.",
"Manage Subscriptions": "Quản lý đăng ký",
"Manage Vendors": "Quản lý Nhà cung cấp",
"Manage your LinuxDO OAuth app": "Quản lý ứng dụng OAuth LinuxDO của bạn",
"Manage your security settings and account access": "Quản lý cài đặt bảo mật và quyền truy cập tài khoản của bạn",
"Manual Disabled": "Vô hiệu hóa thủ công",
"Map fields from the user info response to local user attributes. Supports nested paths (e.g. ocs.data.id).": "Ánh xạ các trường từ phản hồi thông tin người dùng sang thuộc tính người dùng cục bộ. Hỗ trợ đường dẫn lồng nhau (ví dụ: ocs.data.id).",
......@@ -2981,6 +2988,7 @@
"Number of tokens per unit quota": "Số token trên đơn vị hạn mức",
"Number of top log probabilities returned per token": "Số log probabilities hàng đầu trên mỗi token",
"Number of users invited": "Số người dùng được mời",
"OAuth callback URL": "URL callback OAuth",
"OAuth Client ID": "ID Client OAuth",
"OAuth Client Secret": "Bí mật OAuth Client",
"OAuth failed": "OAuth thất bại",
......@@ -3009,6 +3017,7 @@
"OIDC Client ID": "Mã máy khách OIDC",
"OIDC Client Secret": "Mã bí mật ứng dụng khách OIDC",
"OIDC configuration fetched successfully": "Cấu hình OIDC đã được lấy thành công",
"OIDC discovery can fill the endpoint fields automatically when the provider supports it.": "OIDC Discovery có thể tự động điền các trường endpoint khi nhà cung cấp hỗ trợ.",
"OIDC discovery URL. Click \"Auto-discover\" to fetch endpoints automatically.": "URL khám phá OIDC. Nhấp \"Tự động khám phá\" để tự động lấy các endpoint.",
"OIDC endpoints discovered successfully": "Đã khám phá thành công các endpoint OIDC",
"Old Format Template": "Mẫu Định dạng Cũ",
......@@ -3598,6 +3607,7 @@
"redemption codes.": "Mã đổi thưởng",
"Redemption failed": "Đổi thưởng thất bại",
"Redemption successful! Added: {{quota}}": "Đổi mã thành công! Đã thêm: {{quota}}",
"Redirect URL": "URL chuyển hướng",
"Redirecting to chat page...": "Đang chuyển hướng đến trang trò chuyện...",
"Redirecting to Creem checkout...": "Đang chuyển hướng đến thanh toán Creem...",
"Redirecting to GitHub...": "Đang chuyển hướng đến GitHub...",
......@@ -4062,6 +4072,7 @@
"Set tag for selected channels": "Đặt thẻ cho các kênh đã chọn",
"Set the language used across the interface": "Đặt ngôn ngữ sử dụng trong giao diện",
"Set the user's role (cannot be Root)": "Đặt vai trò của người dùng (không được là Root)",
"Set these values in the provider application before enabling login.": "Thiết lập các giá trị này trong ứng dụng của nhà cung cấp trước khi bật đăng nhập.",
"Setting Key": "Khóa cài đặt",
"Setting saved": "Cài đặt đã được lưu",
"Setting up 2FA...": "Đang thiết lập 2FA...",
......@@ -4120,6 +4131,7 @@
"Single Key": "Khóa đơn",
"Site & Branding": "Trang web & thương hiệu",
"Site Key": "Khóa trang web",
"Site URL": "URL trang web",
"Size:": "Kích thước:",
"sk_xxx or rk_xxx": "sk_xxx hoặc rk_xxx",
"Skip async task polling delay": "Bỏ qua độ trễ thăm dò tác vụ bất đồng bộ",
......@@ -4428,6 +4440,7 @@
"This action will permanently remove 2FA protection from your account.": "Hành động này sẽ vĩnh viễn gỡ bỏ tính năng bảo vệ",
"This announcement will be removed from the list.": "Thông báo này sẽ bị xóa khỏi danh sách.",
"This API shortcut will be removed from the list.": "Lối tắt API này sẽ bị xóa khỏi danh sách.",
"This callback URL updates from the slug field and is the value to register with your provider.": "URL callback này cập nhật theo trường slug và là giá trị cần đăng ký với nhà cung cấp.",
"This channel has no configured models.": "Kênh này không có mô hình nào được cấu hình.",
"This channel is not an Ollama channel.": "Kênh này không phải là kênh Ollama.",
"This channel type does not support fetching models": "Loại kênh này không hỗ trợ lấy mô hình",
......@@ -4799,6 +4812,7 @@
"Use the full-width table to scan prices, then select a row to edit it here.": "Duyệt giá trong bảng, rồi chọn một hàng để chỉnh sửa tại đây.",
"Use the group set on the token. If the token has no group, use the user group. The auto group tries the auto assignment order from top to bottom.": "Dùng nhóm đặt trên token. Nếu token không có nhóm, dùng nhóm người dùng. Nhóm auto thử theo thứ tự gán tự động từ trên xuống dưới.",
"Use the pricing group table to manage the ratio and whether the group appears in the token creation dropdown.": "Dùng bảng nhóm định giá để quản lý tỷ lệ và việc nhóm có xuất hiện trong danh sách tạo token hay không.",
"Use this callback URL pattern when registering a custom OAuth provider.": "Dùng mẫu URL callback này khi đăng ký nhà cung cấp OAuth tùy chỉnh.",
"Use this token for API authentication": "Sử dụng token này để xác thực API",
"Use your Passkey": "Sử dụng Passkey của bạn",
"used": "đã sử dụng, cũ",
......
......@@ -491,6 +491,7 @@
"Authentication": "身份驗證",
"Authentication Method": "認證方式",
"Authenticator code": "身份驗證器代碼",
"Authorization callback URL": "授權回呼 URL",
"Authorization Endpoint": "授權端點",
"Authorization Endpoint (Optional)": "授權端點(可選)",
"Authorize": "授權",
......@@ -706,6 +707,7 @@
"Callback Caller IP": "Callback調用者 IP",
"Callback notification URL": "Callback通知地址",
"Callback Payment Method": "Callback支付方式",
"Callback URL format": "回呼 URL 格式",
"Cancel": "取消",
"Cancelled": "已取消",
"Cancelled at": "作廢於",
......@@ -1064,12 +1066,14 @@
"Copy all backup codes": "複製所有備用代碼",
"Copy All Codes": "複製所有代碼",
"Copy API key": "複製 API 金鑰",
"Copy callback URL": "複製回呼 URL",
"Copy Channel": "複製渠道",
"Copy code": "複製代碼",
"Copy Connection Info": "複製連接資訊",
"Copy failed": "複製失敗",
"Copy Field": "複製欄位",
"Copy Header": "複製請求頭",
"Copy homepage URL": "複製首頁 URL",
"Copy Key": "複製金鑰",
"Copy Link": "複製連結",
"Copy model name": "複製模型名稱",
......@@ -1077,6 +1081,7 @@
"Copy prompt": "複製提示詞",
"Copy ready-to-run curl": "複製可直接執行的 curl",
"Copy redemption code": "複製兌換碼",
"Copy redirect URL": "複製重新導向 URL",
"Copy referral link": "複製推薦連結",
"Copy Request Header": "複製請求頭",
"Copy secret key": "複製金鑰",
......@@ -2179,6 +2184,7 @@
"Hit tier": "命中檔位",
"Home": "主頁",
"Home Page Content": "首頁內容",
"Homepage URL": "首頁 URL",
"Hostname or IP of your SMTP provider": "您的 SMTP 供應商的主機名稱或 IP",
"Hour": "小時",
"Hour of day": "小時",
......@@ -2511,6 +2517,7 @@
"Manage subscription plans and pricing.": "管理訂閱計劃和定價。",
"Manage Subscriptions": "管理訂閱",
"Manage Vendors": "管理供應商",
"Manage your LinuxDO OAuth app": "管理你的 LinuxDO OAuth 應用程式",
"Manage your security settings and account access": "管理您的安全設定和用戶存取",
"Manual Disabled": "手動停用",
"Map fields from the user info response to local user attributes. Supports nested paths (e.g. ocs.data.id).": "將用戶資訊回應中的欄位映射到本地用戶屬性。支援嵌套路徑(例如 ocs.data.id)。",
......@@ -2981,6 +2988,7 @@
"Number of tokens per unit quota": "每單位配額的令牌數",
"Number of top log probabilities returned per token": "每個 token 返回的 top 概率數量",
"Number of users invited": "已邀請的用戶數量",
"OAuth callback URL": "OAuth 回呼 URL",
"OAuth Client ID": "OAuth 用戶端 ID",
"OAuth Client Secret": "OAuth 用戶端密鑰",
"OAuth failed": "OAuth 失敗",
......@@ -3009,6 +3017,7 @@
"OIDC Client ID": "OIDC 用戶端 ID",
"OIDC Client Secret": "OIDC 用戶端密鑰",
"OIDC configuration fetched successfully": "OIDC 設定獲取成功",
"OIDC discovery can fill the endpoint fields automatically when the provider supports it.": "提供商支援時,OIDC Discovery 可以自動填入端點欄位。",
"OIDC discovery URL. Click \"Auto-discover\" to fetch endpoints automatically.": "OIDC 發現 URL。點擊「自動發現」以自動獲取端點。",
"OIDC endpoints discovered successfully": "OIDC 端點發現成功",
"Old Format Template": "舊格式模板",
......@@ -3598,6 +3607,7 @@
"redemption codes.": "兌換碼。",
"Redemption failed": "兌換失敗",
"Redemption successful! Added: {{quota}}": "兌換成功!已新增:{{quota}}",
"Redirect URL": "重新導向 URL",
"Redirecting to chat page...": "正在跳轉到聊天頁面...",
"Redirecting to Creem checkout...": "正在重新導向到 Creem 結帳...",
"Redirecting to GitHub...": "正在跳轉 GitHub...",
......@@ -4062,6 +4072,7 @@
"Set tag for selected channels": "為選定的渠道設定標籤",
"Set the language used across the interface": "設定介面顯示語言",
"Set the user's role (cannot be Root)": "設定用戶角色(不能是 Root)",
"Set these values in the provider application before enabling login.": "啟用登入前,請先在提供商應用程式中設定這些值。",
"Setting Key": "設定項",
"Setting saved": "設定已儲存",
"Setting up 2FA...": "正在設定 2FA...",
......@@ -4120,6 +4131,7 @@
"Single Key": "單金鑰",
"Site & Branding": "站點與品牌",
"Site Key": "站點金鑰",
"Site URL": "網站地址",
"Size:": "大小:",
"sk_xxx or rk_xxx": "sk_xxx 或 rk_xxx",
"Skip async task polling delay": "跳過異步任務輪詢延遲",
......@@ -4428,6 +4440,7 @@
"This action will permanently remove 2FA protection from your account.": "此操作將永久移除您用戶的 2FA 保護。",
"This announcement will be removed from the list.": "此公告將從列表中移除。",
"This API shortcut will be removed from the list.": "此 API 快捷方式將從列表中移除。",
"This callback URL updates from the slug field and is the value to register with your provider.": "此回呼 URL 會依 slug 欄位即時更新,請將它註冊到你的提供商。",
"This channel has no configured models.": "該渠道沒有設定模型。",
"This channel is not an Ollama channel.": "該渠道不是 Ollama 渠道。",
"This channel type does not support fetching models": "此渠道類型不支援獲取模型",
......@@ -4799,6 +4812,7 @@
"Use the full-width table to scan prices, then select a row to edit it here.": "先在表格中快速瀏覽價格,然後選擇一行在這裡編輯。",
"Use the group set on the token. If the token has no group, use the user group. The auto group tries the auto assignment order from top to bottom.": "使用令牌上設定的分組;令牌未設定分組時,使用用戶分組。auto 分組會按自動分組順序從上到下嘗試。",
"Use the pricing group table to manage the ratio and whether the group appears in the token creation dropdown.": "使用定價分組表管理倍率,以及該分組是否出現在建立令牌的下拉框中。",
"Use this callback URL pattern when registering a custom OAuth provider.": "註冊自訂 OAuth 提供商時使用此回呼 URL 格式。",
"Use this token for API authentication": "使用此令牌進行 API 身份驗證",
"Use your Passkey": "使用您的通行金鑰",
"used": "已使用",
......
......@@ -491,6 +491,7 @@
"Authentication": "身份验证",
"Authentication Method": "认证方式",
"Authenticator code": "身份验证器代码",
"Authorization callback URL": "授权回调 URL",
"Authorization Endpoint": "授权端点",
"Authorization Endpoint (Optional)": "授权端点(可选)",
"Authorize": "授权",
......@@ -706,6 +707,7 @@
"Callback Caller IP": "回调调用者 IP",
"Callback notification URL": "回调通知地址",
"Callback Payment Method": "回调支付方式",
"Callback URL format": "回调 URL 格式",
"Cancel": "取消",
"Cancelled": "已取消",
"Cancelled at": "作废于",
......@@ -1064,12 +1066,14 @@
"Copy all backup codes": "复制所有备份代码",
"Copy All Codes": "复制所有代码",
"Copy API key": "复制 API 密钥",
"Copy callback URL": "复制回调 URL",
"Copy Channel": "复制渠道",
"Copy code": "复制代码",
"Copy Connection Info": "复制连接信息",
"Copy failed": "复制失败",
"Copy Field": "复制字段",
"Copy Header": "复制请求头",
"Copy homepage URL": "复制主页 URL",
"Copy Key": "复制密钥",
"Copy Link": "复制链接",
"Copy model name": "复制模型名称",
......@@ -1077,6 +1081,7 @@
"Copy prompt": "复制提示词",
"Copy ready-to-run curl": "复制可直接运行的 curl",
"Copy redemption code": "复制兑换码",
"Copy redirect URL": "复制重定向 URL",
"Copy referral link": "复制推荐链接",
"Copy Request Header": "复制请求头",
"Copy secret key": "复制密钥",
......@@ -2179,6 +2184,7 @@
"Hit tier": "命中档位",
"Home": "主页",
"Home Page Content": "首页内容",
"Homepage URL": "主页 URL",
"Hostname or IP of your SMTP provider": "您的 SMTP 提供商的主机名或 IP",
"Hour": "小时",
"Hour of day": "小时",
......@@ -2511,6 +2517,7 @@
"Manage subscription plans and pricing.": "管理订阅计划和定价。",
"Manage Subscriptions": "管理订阅",
"Manage Vendors": "管理供应商",
"Manage your LinuxDO OAuth app": "管理你的 LinuxDO OAuth 应用",
"Manage your security settings and account access": "管理您的安全设置和账户访问",
"Manual Disabled": "手动禁用",
"Map fields from the user info response to local user attributes. Supports nested paths (e.g. ocs.data.id).": "将用户信息响应中的字段映射到本地用户属性。支持嵌套路径(例如 ocs.data.id)。",
......@@ -2981,6 +2988,7 @@
"Number of tokens per unit quota": "每单位配额的令牌数",
"Number of top log probabilities returned per token": "每个 token 返回的 top 概率数量",
"Number of users invited": "已邀请的用户数量",
"OAuth callback URL": "OAuth 回调 URL",
"OAuth Client ID": "OAuth 客户端 ID",
"OAuth Client Secret": "OAuth 客户端密钥",
"OAuth failed": "OAuth 失败",
......@@ -3009,6 +3017,7 @@
"OIDC Client ID": "OIDC 客户端 ID",
"OIDC Client Secret": "OIDC 客户端密钥",
"OIDC configuration fetched successfully": "OIDC 配置获取成功",
"OIDC discovery can fill the endpoint fields automatically when the provider supports it.": "当提供商支持时,OIDC Discovery 可以自动填充端点字段。",
"OIDC discovery URL. Click \"Auto-discover\" to fetch endpoints automatically.": "OIDC 发现 URL。点击\"自动发现\"以自动获取端点。",
"OIDC endpoints discovered successfully": "OIDC 端点发现成功",
"Old Format Template": "旧格式模板",
......@@ -3598,6 +3607,7 @@
"redemption codes.": "兑换码。",
"Redemption failed": "兑换失败",
"Redemption successful! Added: {{quota}}": "兑换成功!已添加:{{quota}}",
"Redirect URL": "重定向 URL",
"Redirecting to chat page...": "正在跳转到聊天页面...",
"Redirecting to Creem checkout...": "正在重定向到 Creem 结账...",
"Redirecting to GitHub...": "正在跳转 GitHub...",
......@@ -4062,6 +4072,7 @@
"Set tag for selected channels": "为选定的渠道设置标签",
"Set the language used across the interface": "设置界面显示语言",
"Set the user's role (cannot be Root)": "设置用户角色(不能是 Root)",
"Set these values in the provider application before enabling login.": "启用登录前,请先在提供商应用中设置这些值。",
"Setting Key": "配置项",
"Setting saved": "设置已保存",
"Setting up 2FA...": "正在设置 2FA...",
......@@ -4120,6 +4131,7 @@
"Single Key": "单密钥",
"Site & Branding": "站点与品牌",
"Site Key": "站点密钥",
"Site URL": "网站地址",
"Size:": "大小:",
"sk_xxx or rk_xxx": "sk_xxx 或 rk_xxx",
"Skip async task polling delay": "跳过异步任务轮询延迟",
......@@ -4428,6 +4440,7 @@
"This action will permanently remove 2FA protection from your account.": "此操作将永久移除您账户的 2FA 保护。",
"This announcement will be removed from the list.": "此公告将从列表中移除。",
"This API shortcut will be removed from the list.": "此 API 快捷方式将从列表中移除。",
"This callback URL updates from the slug field and is the value to register with your provider.": "此回调 URL 会根据 slug 字段实时更新,请将它注册到你的提供商。",
"This channel has no configured models.": "该渠道没有配置模型。",
"This channel is not an Ollama channel.": "该渠道不是 Ollama 渠道。",
"This channel type does not support fetching models": "此渠道类型不支持获取模型",
......@@ -4799,6 +4812,7 @@
"Use the full-width table to scan prices, then select a row to edit it here.": "先在表格中快速浏览价格,然后选择一行在这里编辑。",
"Use the group set on the token. If the token has no group, use the user group. The auto group tries the auto assignment order from top to bottom.": "使用令牌上设置的分组;令牌未设置分组时,使用用户分组。auto 分组会按自动分组顺序从上到下尝试。",
"Use the pricing group table to manage the ratio and whether the group appears in the token creation dropdown.": "使用定价分组表管理倍率,以及该分组是否出现在创建令牌的下拉框中。",
"Use this callback URL pattern when registering a custom OAuth provider.": "注册自定义 OAuth 提供商时使用此回调 URL 格式。",
"Use this token for API authentication": "使用此令牌进行 API 身份验证",
"Use your Passkey": "使用您的通行密钥",
"used": "已使用",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment