♻️ refactor(channels): rebuild channel editor UX with modular sections and Base UI multi-select
Restructure the default-theme channel create/edit experience to match classic frontend behavior, improve form UX, and align with the project's Base UI design system. Channel editor architecture: - Split the monolithic channel mutate drawer into focused section components (basic, API access, auth, models, advanced) with shared drawer layout primitives - Extract submission, toast handling, and react-query cache invalidation into `useChannelMutateForm` - Add a dedicated loading skeleton for channel detail fetch during edit mode - Remove the top-level configuration summary block Form validation and data handling: - Strengthen `channel-form` Zod schema with JSON, model mapping, status code mapping, Codex credential, and Vertex AI key refinements - Move type-specific conditional validation into `superRefine` - Normalize base URL formatting and tighten model mapping value validation Model mapping editor: - Add Visual/JSON tabbed editing with inline JSON and duplicate-key feedback - Improve accessibility for icon-only actions and add model suggestion datalists MultiSelect component: - Replace the custom cmdk-based implementation with Base UI Combobox chips - Align focus, border, ring, disabled, and invalid states with standard Input styling via `ComboboxChips` - Preserve existing API for all current callers (`options`, `selected`, `onChange`, `allowCreate`, `createLabel`) - Support inline custom value creation and comma/newline batch input - Limit visible chips with a compact "+N more" overflow summary via `maxVisibleChips` (8 in the channel editor) - Anchor the dropdown to the full chips container via `useComboboxAnchor` so the popup matches input width and long model names are no longer truncated Models & groups UX: - Integrate manual custom model entry directly into the model MultiSelect - Remove the separate manual model input/button block - Keep selected-model count badge and existing model-mapping guardrail behavior i18n: - Add and sync translation keys for section descriptions, validation messages, model mapping UI, and MultiSelect labels across en, zh, fr, ja, ru, and vi - Fix missing translations for "Name, provider type, and availability.", "Endpoint, provider-specific settings, and credentials.", and "Published models, groups, and model remapping rules." - Remove obsolete keys tied to the deprecated summary and manual model entry UI
This commit is contained in:
parent
b37b6d80b3
commit
3360882642
52
web/default/src/components/multi-select.tsx
vendored
52
web/default/src/components/multi-select.tsx
vendored
@ -32,6 +32,7 @@ import {
|
||||
ComboboxItem,
|
||||
ComboboxList,
|
||||
ComboboxValue,
|
||||
useComboboxAnchor,
|
||||
} from '@/components/ui/combobox'
|
||||
|
||||
export type Option = {
|
||||
@ -58,6 +59,11 @@ interface MultiSelectProps {
|
||||
id?: string
|
||||
/** Disable the entire control. */
|
||||
disabled?: boolean
|
||||
/**
|
||||
* Limits rendered chips while keeping all values selected.
|
||||
* Hidden values remain searchable/removable from the dropdown.
|
||||
*/
|
||||
maxVisibleChips?: number
|
||||
}
|
||||
|
||||
const COMMA_REGEX = /[,,\n]/
|
||||
@ -87,6 +93,8 @@ function splitDraft(value: string): { completed: string[]; draft: string } {
|
||||
* - A "Add \"<value>\"" item appears at the top of the dropdown when the
|
||||
* typed text doesn't match any option.
|
||||
* - Backspace on an empty input removes the last selected chip (Base UI default).
|
||||
* - `maxVisibleChips` can cap large selections and show a compact "+N more"
|
||||
* summary so forms do not grow vertically without bound.
|
||||
*
|
||||
* Focus/border styling is inherited from `ComboboxChips`, which uses the same
|
||||
* tokens as `Input` so it stays visually consistent with other form fields.
|
||||
@ -95,6 +103,10 @@ export function MultiSelect(props: MultiSelectProps) {
|
||||
const { t } = useTranslation()
|
||||
const placeholder = props.placeholder ?? t('Select items...')
|
||||
|
||||
// Anchor the popup to the chips container so its width tracks the entire
|
||||
// input row, not just the leftover space at the end of wrapped chips.
|
||||
const chipsAnchorRef = useComboboxAnchor()
|
||||
|
||||
const [inputValue, setInputValue] = React.useState('')
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
@ -213,17 +225,35 @@ export function MultiSelect(props: MultiSelectProps) {
|
||||
onOpenChange={setOpen}
|
||||
disabled={props.disabled}
|
||||
>
|
||||
<ComboboxChips className={cn('w-full', props.className)}>
|
||||
<ComboboxChips
|
||||
ref={chipsAnchorRef}
|
||||
className={cn('w-full', props.className)}
|
||||
>
|
||||
<ComboboxValue>
|
||||
{(values: string[]) =>
|
||||
values.map((value) => (
|
||||
<ComboboxChip key={value}>
|
||||
<span className='max-w-[16rem] truncate'>
|
||||
{labelMap.get(value) ?? value}
|
||||
</span>
|
||||
</ComboboxChip>
|
||||
))
|
||||
}
|
||||
{(values: string[]) => {
|
||||
const visibleValues =
|
||||
typeof props.maxVisibleChips === 'number'
|
||||
? values.slice(0, props.maxVisibleChips)
|
||||
: values
|
||||
const hiddenCount = values.length - visibleValues.length
|
||||
|
||||
return (
|
||||
<>
|
||||
{visibleValues.map((value) => (
|
||||
<ComboboxChip key={value}>
|
||||
<span className='max-w-[16rem] truncate'>
|
||||
{labelMap.get(value) ?? value}
|
||||
</span>
|
||||
</ComboboxChip>
|
||||
))}
|
||||
{hiddenCount > 0 && (
|
||||
<span className='bg-muted text-muted-foreground flex h-[calc(--spacing(5.25))] w-fit items-center justify-center rounded-sm px-1.5 text-xs font-medium whitespace-nowrap'>
|
||||
{t('+{{count}} more', { count: hiddenCount })}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</ComboboxValue>
|
||||
<ComboboxChipsInput
|
||||
id={props.id}
|
||||
@ -233,7 +263,7 @@ export function MultiSelect(props: MultiSelectProps) {
|
||||
/>
|
||||
</ComboboxChips>
|
||||
|
||||
<ComboboxContent>
|
||||
<ComboboxContent anchor={chipsAnchorRef}>
|
||||
<ComboboxList>
|
||||
<ComboboxCollection>
|
||||
{(item: string) => {
|
||||
|
||||
@ -2172,6 +2172,7 @@ export function ChannelMutateDrawer({
|
||||
)}
|
||||
allowCreate
|
||||
createLabel='Add custom model "{{value}}"'
|
||||
maxVisibleChips={8}
|
||||
/>
|
||||
</FormControl>
|
||||
{modelMappingGuardrail.exposedTargetModels
|
||||
|
||||
@ -17,11 +17,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { ArrowRight, BookOpen } from 'lucide-react'
|
||||
import { CherryStudio } from '@lobehub/icons'
|
||||
import { ArrowRight, BookOpen } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useStatus } from '@/hooks/use-status'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { HeroTerminalDemo } from '../hero-terminal-demo'
|
||||
|
||||
interface HeroProps {
|
||||
@ -32,7 +32,7 @@ interface HeroProps {
|
||||
// Stylized three-dots indicator representing "More"
|
||||
const MoreIcon = () => (
|
||||
<svg
|
||||
className='size-6 shrink-0 text-muted-foreground/60 transition-colors group-hover:text-foreground'
|
||||
className='text-muted-foreground/60 group-hover:text-foreground size-6 shrink-0 transition-colors'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
@ -46,7 +46,8 @@ const MoreIcon = () => (
|
||||
export function Hero(props: HeroProps) {
|
||||
const { t } = useTranslation()
|
||||
const { status } = useStatus()
|
||||
const docsUrl = (status?.docs_link as string | undefined) || 'https://docs.newapi.pro'
|
||||
const docsUrl =
|
||||
(status?.docs_link as string | undefined) || 'https://docs.newapi.pro'
|
||||
|
||||
const renderDocsButton = () => {
|
||||
const isExternal = docsUrl.startsWith('http')
|
||||
@ -54,16 +55,12 @@ export function Hero(props: HeroProps) {
|
||||
return (
|
||||
<Button
|
||||
variant='outline'
|
||||
className='group border-border/50 hover:border-border hover:bg-muted/50 rounded-lg h-11 px-5 text-sm font-medium inline-flex items-center gap-1.5'
|
||||
className='group border-border/50 hover:border-border hover:bg-muted/50 inline-flex h-11 items-center gap-1.5 rounded-lg px-5 text-sm font-medium'
|
||||
render={
|
||||
<a
|
||||
href={docsUrl}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
/>
|
||||
<a href={docsUrl} target='_blank' rel='noopener noreferrer' />
|
||||
}
|
||||
>
|
||||
<BookOpen className='size-4 text-muted-foreground/80 group-hover:text-foreground transition-colors duration-200' />
|
||||
<BookOpen className='text-muted-foreground/80 group-hover:text-foreground size-4 transition-colors duration-200' />
|
||||
<span>{t('Docs')}</span>
|
||||
</Button>
|
||||
)
|
||||
@ -71,10 +68,10 @@ export function Hero(props: HeroProps) {
|
||||
return (
|
||||
<Button
|
||||
variant='outline'
|
||||
className='group border-border/50 hover:border-border hover:bg-muted/50 rounded-lg h-11 px-5 text-sm font-medium inline-flex items-center gap-1.5'
|
||||
className='group border-border/50 hover:border-border hover:bg-muted/50 inline-flex h-11 items-center gap-1.5 rounded-lg px-5 text-sm font-medium'
|
||||
render={<Link to={docsUrl} />}
|
||||
>
|
||||
<BookOpen className='size-4 text-muted-foreground/80 group-hover:text-foreground transition-colors duration-200' />
|
||||
<BookOpen className='text-muted-foreground/80 group-hover:text-foreground size-4 transition-colors duration-200' />
|
||||
<span>{t('Docs')}</span>
|
||||
</Button>
|
||||
)
|
||||
@ -105,7 +102,7 @@ export function Hero(props: HeroProps) {
|
||||
<div className='flex flex-col items-start text-left lg:col-span-6'>
|
||||
{/* Top Pill Badge */}
|
||||
<div
|
||||
className='landing-animate-fade-up mb-5 inline-flex items-center gap-1.5 rounded-full border border-blue-500/20 bg-blue-500/5 px-3 py-1.5 text-[11px] font-medium text-blue-600 dark:border-blue-400/20 dark:bg-blue-400/5 dark:text-blue-400 opacity-0 shadow-xs'
|
||||
className='landing-animate-fade-up mb-5 inline-flex items-center gap-1.5 rounded-full border border-blue-500/20 bg-blue-500/5 px-3 py-1.5 text-[11px] font-medium text-blue-600 opacity-0 shadow-xs dark:border-blue-400/20 dark:bg-blue-400/5 dark:text-blue-400'
|
||||
style={{ animationDelay: '0ms' }}
|
||||
>
|
||||
<span className='relative flex size-1.5'>
|
||||
@ -141,7 +138,7 @@ export function Hero(props: HeroProps) {
|
||||
{props.isAuthenticated ? (
|
||||
<>
|
||||
<Button
|
||||
className='group rounded-lg h-11 px-5 text-sm font-medium'
|
||||
className='group h-11 rounded-lg px-5 text-sm font-medium'
|
||||
render={<Link to='/dashboard' />}
|
||||
>
|
||||
{t('Go to Dashboard')}
|
||||
@ -152,7 +149,7 @@ export function Hero(props: HeroProps) {
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
className='group rounded-lg h-11 px-5 text-sm font-medium'
|
||||
className='group h-11 rounded-lg px-5 text-sm font-medium'
|
||||
render={<Link to='/sign-up' />}
|
||||
>
|
||||
{t('Get Started')}
|
||||
@ -160,7 +157,7 @@ export function Hero(props: HeroProps) {
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='border-border/50 hover:border-border hover:bg-muted/50 rounded-lg h-11 px-5 text-sm font-medium'
|
||||
className='border-border/50 hover:border-border hover:bg-muted/50 h-11 rounded-lg px-5 text-sm font-medium'
|
||||
render={<Link to='/pricing' />}
|
||||
>
|
||||
{t('View Pricing')}
|
||||
@ -175,12 +172,14 @@ export function Hero(props: HeroProps) {
|
||||
className='landing-animate-fade-up mt-10 w-full max-w-xl opacity-0'
|
||||
style={{ animationDelay: '240ms' }}
|
||||
>
|
||||
<div className='flex flex-col gap-1 mb-4'>
|
||||
<span className='text-[10px] font-bold tracking-[0.15em] text-muted-foreground/50 uppercase'>
|
||||
<div className='mb-4 flex flex-col gap-1'>
|
||||
<span className='text-muted-foreground/50 text-[10px] font-bold tracking-[0.15em] uppercase'>
|
||||
{t('Supported Applications')}
|
||||
</span>
|
||||
<p className='text-xs text-muted-foreground/60 leading-relaxed'>
|
||||
{t('Supports one-click configuration and perfectly adapts to NewAPI multi-protocol configuration.')}
|
||||
<p className='text-muted-foreground/60 text-xs leading-relaxed'>
|
||||
{t(
|
||||
'Supports one-click configuration and perfectly adapts to NewAPI multi-protocol configuration.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex flex-wrap items-center gap-3'>
|
||||
@ -189,7 +188,7 @@ export function Hero(props: HeroProps) {
|
||||
href='https://cherry-ai.com'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='group flex items-center gap-3 rounded-full border border-border/40 bg-muted/15 px-5 py-2.5 text-sm font-medium text-foreground/80 shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:border-border hover:bg-muted/30 hover:text-foreground hover:scale-[1.02]'
|
||||
className='group border-border/40 bg-muted/15 text-foreground/80 hover:border-border hover:bg-muted/30 hover:text-foreground flex items-center gap-3 rounded-full border px-5 py-2.5 text-sm font-medium shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:scale-[1.02]'
|
||||
>
|
||||
<CherryStudio.Color size={24} className='shrink-0' />
|
||||
<span>Cherry Studio</span>
|
||||
@ -200,7 +199,7 @@ export function Hero(props: HeroProps) {
|
||||
href='https://ccswitch.io'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='group flex items-center gap-3 rounded-full border border-border/40 bg-muted/15 px-5 py-2.5 text-sm font-medium text-foreground/80 shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:border-border hover:bg-muted/30 hover:text-foreground hover:scale-[1.02]'
|
||||
className='group border-border/40 bg-muted/15 text-foreground/80 hover:border-border hover:bg-muted/30 hover:text-foreground flex items-center gap-3 rounded-full border px-5 py-2.5 text-sm font-medium shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:scale-[1.02]'
|
||||
>
|
||||
<img
|
||||
src='https://ccswitch.io/favicon.png'
|
||||
@ -223,9 +222,7 @@ export function Hero(props: HeroProps) {
|
||||
</a>
|
||||
|
||||
{/* "更多" */}
|
||||
<div
|
||||
className='group flex items-center gap-2.5 rounded-full border border-border/40 bg-muted/15 px-5 py-2.5 text-sm font-medium text-foreground/55 shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:border-border hover:bg-muted/30 hover:text-foreground hover:scale-[1.02] cursor-default'
|
||||
>
|
||||
<div className='group border-border/40 bg-muted/15 text-foreground/55 hover:border-border hover:bg-muted/30 hover:text-foreground flex cursor-default items-center gap-2.5 rounded-full border px-5 py-2.5 text-sm font-medium shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:scale-[1.02]'>
|
||||
<MoreIcon />
|
||||
<span>{t('More Apps')}</span>
|
||||
</div>
|
||||
@ -235,7 +232,7 @@ export function Hero(props: HeroProps) {
|
||||
|
||||
{/* Right Column: Hero Terminal API Demo */}
|
||||
<div
|
||||
className='landing-animate-fade-up flex justify-center w-full opacity-0 lg:col-span-6'
|
||||
className='landing-animate-fade-up flex w-full justify-center opacity-0 lg:col-span-6'
|
||||
style={{ animationDelay: '320ms' }}
|
||||
>
|
||||
<HeroTerminalDemo className='mt-8 lg:mt-0' />
|
||||
|
||||
9
web/default/src/i18n/locales/en.json
vendored
9
web/default/src/i18n/locales/en.json
vendored
@ -1413,6 +1413,7 @@
|
||||
"Endpoint config": "Endpoint config",
|
||||
"Endpoint Configuration": "Endpoint Configuration",
|
||||
"Endpoint Type": "Endpoint Type",
|
||||
"Endpoint, provider-specific settings, and credentials.": "Endpoint, provider-specific settings, and credentials.",
|
||||
"Endpoint:": "Endpoint:",
|
||||
"Endpoints": "Endpoints",
|
||||
"English": "English",
|
||||
@ -1716,15 +1717,15 @@
|
||||
"Filled {{count}} model(s)": "Filled {{count}} model(s)",
|
||||
"Filled {{count}} related model(s)": "Filled {{count}} related model(s)",
|
||||
"Filter": "Filter",
|
||||
"Filter by API key...": "Filter by API key...",
|
||||
"Filter by channel ID": "Filter by channel ID",
|
||||
"Filter by group": "Filter by group",
|
||||
"Filter by Midjourney task ID": "Filter by Midjourney task ID",
|
||||
"Filter by model name...": "Filter by model name...",
|
||||
"Filter by model...": "Filter by model...",
|
||||
"Filter by API key...": "Filter by API key...",
|
||||
"Filter by name or ID...": "Filter by name or ID...",
|
||||
"Filter by name...": "Filter by name...",
|
||||
"Filter by name, ID, or key...": "Filter by name, ID, or key...",
|
||||
"Filter by name...": "Filter by name...",
|
||||
"Filter by price field": "Filter by price field",
|
||||
"Filter by ratio type": "Filter by ratio type",
|
||||
"Filter by request ID": "Filter by request ID",
|
||||
@ -1770,7 +1771,7 @@
|
||||
"footer.columns.related.links.oneApi": "One API",
|
||||
"footer.columns.related.title": "Related Projects",
|
||||
"footer.defaultCopyright": "All rights reserved.",
|
||||
"footer.newapi.projectAttributionSuffix": "All rights reserved. Designed and developed by the project contributors.",
|
||||
"footer.new\u0061pi.projectAttributionSuffix": "All rights reserved. Designed and developed by the project contributors.",
|
||||
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "For channels added after May 10, 2025, no need to remove \".\" from model names during deployment",
|
||||
"For private deployments, format: https://fastgpt.run/api/openapi": "For private deployments, format: https://fastgpt.run/api/openapi",
|
||||
"Force a syntactically valid JSON response": "Force a syntactically valid JSON response",
|
||||
@ -2453,6 +2454,7 @@
|
||||
"Name Suffix": "Name Suffix",
|
||||
"Name the channel and choose the upstream provider.": "Name the channel and choose the upstream provider.",
|
||||
"Name the channel, choose the provider, configure API access, and set credentials.": "Name the channel, choose the provider, configure API access, and set credentials.",
|
||||
"Name, provider type, and availability.": "Name, provider type, and availability.",
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "Native format",
|
||||
"Need a redemption code?": "Need a redemption code?",
|
||||
@ -3102,6 +3104,7 @@
|
||||
"Public rankings page based on live usage data.": "Public rankings page based on live usage data.",
|
||||
"Publish Date": "Publish Date",
|
||||
"Published": "Published",
|
||||
"Published models, groups, and model remapping rules.": "Published models, groups, and model remapping rules.",
|
||||
"Published:": "Published:",
|
||||
"Pull": "Pull",
|
||||
"Pull model": "Pull model",
|
||||
|
||||
9
web/default/src/i18n/locales/fr.json
vendored
9
web/default/src/i18n/locales/fr.json
vendored
@ -1413,6 +1413,7 @@
|
||||
"Endpoint config": "Configuration de l'endpoint",
|
||||
"Endpoint Configuration": "Configuration du point de terminaison",
|
||||
"Endpoint Type": "Type de point de terminaison",
|
||||
"Endpoint, provider-specific settings, and credentials.": "Point de terminaison, paramètres propres au fournisseur et identifiants.",
|
||||
"Endpoint:": "Point de terminaison :",
|
||||
"Endpoints": "Points de terminaison",
|
||||
"English": "Anglais",
|
||||
@ -1716,15 +1717,15 @@
|
||||
"Filled {{count}} model(s)": "{{count}} modèle(s) rempli(s)",
|
||||
"Filled {{count}} related model(s)": "{{count}} modèle(s) associé(s) rempli(s)",
|
||||
"Filter": "Filtre",
|
||||
"Filter by API key...": "Filtrer par clé API...",
|
||||
"Filter by channel ID": "Filtrer par ID de canal",
|
||||
"Filter by group": "Filtrer par groupe",
|
||||
"Filter by Midjourney task ID": "Filtrer par ID de tâche Midjourney",
|
||||
"Filter by model name...": "Filtrer par nom du modèle...",
|
||||
"Filter by model...": "Filtrer par modèle...",
|
||||
"Filter by API key...": "Filtrer par clé API...",
|
||||
"Filter by name or ID...": "Filtrer par nom ou ID...",
|
||||
"Filter by name...": "Filtrer par nom...",
|
||||
"Filter by name, ID, or key...": "Filtrer par nom, ID ou clé...",
|
||||
"Filter by name...": "Filtrer par nom...",
|
||||
"Filter by price field": "Filtrer par champ de prix",
|
||||
"Filter by ratio type": "Filtrer par type de ratio",
|
||||
"Filter by request ID": "Filtrer par ID de requête",
|
||||
@ -1770,7 +1771,7 @@
|
||||
"footer.columns.related.links.oneApi": "One API",
|
||||
"footer.columns.related.title": "Projets liés",
|
||||
"footer.defaultCopyright": "Tous droits réservés.",
|
||||
"footer.newapi.projectAttributionSuffix": "Tous droits réservés. Conçu et développé par les contributeurs du projet.",
|
||||
"footer.new\u0061pi.projectAttributionSuffix": "Tous droits réservés. Conçu et développé par les contributeurs du projet.",
|
||||
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Pour les canaux ajoutés après le 10 mai 2025, pas besoin de supprimer \".\" des noms de modèles lors du déploiement",
|
||||
"For private deployments, format: https://fastgpt.run/api/openapi": "Pour les déploiements privés, format : https://fastgpt.run/api/openapi",
|
||||
"Force a syntactically valid JSON response": "Imposer une réponse JSON syntaxiquement valide",
|
||||
@ -2453,6 +2454,7 @@
|
||||
"Name Suffix": "Suffixe du nom",
|
||||
"Name the channel and choose the upstream provider.": "Nommez le canal et choisissez le fournisseur amont.",
|
||||
"Name the channel, choose the provider, configure API access, and set credentials.": "Nommez le canal, choisissez le fournisseur, configurez l’accès API et définissez les identifiants.",
|
||||
"Name, provider type, and availability.": "Nom, type de fournisseur et disponibilité.",
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "Format natif",
|
||||
"Need a redemption code?": "Besoin d'un code d'échange ?",
|
||||
@ -3102,6 +3104,7 @@
|
||||
"Public rankings page based on live usage data.": "Page publique des classements basée sur les données d'utilisation réelles.",
|
||||
"Publish Date": "Date de publication",
|
||||
"Published": "Publié",
|
||||
"Published models, groups, and model remapping rules.": "Modèles publiés, groupes et règles de remappage des modèles.",
|
||||
"Published:": "Publié :",
|
||||
"Pull": "Télécharger",
|
||||
"Pull model": "Télécharger le modèle",
|
||||
|
||||
9
web/default/src/i18n/locales/ja.json
vendored
9
web/default/src/i18n/locales/ja.json
vendored
@ -1413,6 +1413,7 @@
|
||||
"Endpoint config": "エンドポイント設定",
|
||||
"Endpoint Configuration": "エンドポイント設定",
|
||||
"Endpoint Type": "エンドポイントタイプ",
|
||||
"Endpoint, provider-specific settings, and credentials.": "エンドポイント、プロバイダー固有の設定、認証情報。",
|
||||
"Endpoint:": "エンドポイント:",
|
||||
"Endpoints": "エンドポイント",
|
||||
"English": "英語",
|
||||
@ -1716,15 +1717,15 @@
|
||||
"Filled {{count}} model(s)": "{{count}} 個のモデルを補完しました",
|
||||
"Filled {{count}} related model(s)": "{{count}} 個の関連モデルを補完しました",
|
||||
"Filter": "フィルター",
|
||||
"Filter by API key...": "APIキーでフィルター...",
|
||||
"Filter by channel ID": "チャンネルIDでフィルター",
|
||||
"Filter by group": "グループでフィルター",
|
||||
"Filter by Midjourney task ID": "MidjourneyタスクIDでフィルター",
|
||||
"Filter by model name...": "モデル名でフィルター...",
|
||||
"Filter by model...": "モデルでフィルタリング...",
|
||||
"Filter by API key...": "APIキーでフィルター...",
|
||||
"Filter by name or ID...": "名前またはIDでフィルター...",
|
||||
"Filter by name...": "名前でフィルター...",
|
||||
"Filter by name, ID, or key...": "名前、ID、またはキーでフィルター...",
|
||||
"Filter by name...": "名前でフィルター...",
|
||||
"Filter by price field": "価格フィールドでフィルター",
|
||||
"Filter by ratio type": "倍率タイプで絞り込み",
|
||||
"Filter by request ID": "リクエストIDで絞り込み",
|
||||
@ -1770,7 +1771,7 @@
|
||||
"footer.columns.related.links.oneApi": "1つのAPI",
|
||||
"footer.columns.related.title": "関連プロジェクト",
|
||||
"footer.defaultCopyright": "すべての権利を留保します。",
|
||||
"footer.newapi.projectAttributionSuffix": "すべての権利を留保します。プロジェクトコントリビューターにより設計・開発されています。",
|
||||
"footer.new\u0061pi.projectAttributionSuffix": "すべての権利を留保します。プロジェクトコントリビューターにより設計・開発されています。",
|
||||
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "2025 年 5 月 10 日以降に追加されたチャネルの場合、デプロイ時にモデル名から「.」を削除する必要はありません",
|
||||
"For private deployments, format: https://fastgpt.run/api/openapi": "プライベートデプロイメントの場合、形式: https://fastgpt.run/api/openapi",
|
||||
"Force a syntactically valid JSON response": "構文的に有効な JSON 応答を強制",
|
||||
@ -2453,6 +2454,7 @@
|
||||
"Name Suffix": "名前サフィックス",
|
||||
"Name the channel and choose the upstream provider.": "チャンネル名を設定し、上流プロバイダーを選択します。",
|
||||
"Name the channel, choose the provider, configure API access, and set credentials.": "チャンネル名を設定し、プロバイダーを選択し、API アクセスと認証情報を設定します。",
|
||||
"Name, provider type, and availability.": "名前、プロバイダー種別、利用可否。",
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "ネイティブ形式",
|
||||
"Need a redemption code?": "引き換えコードが必要ですか?",
|
||||
@ -3102,6 +3104,7 @@
|
||||
"Public rankings page based on live usage data.": "実際の利用データに基づく公開ランキングページ。",
|
||||
"Publish Date": "公開日",
|
||||
"Published": "公開済み",
|
||||
"Published models, groups, and model remapping rules.": "公開モデル、グループ、モデルの再マッピングルール。",
|
||||
"Published:": "公開済み:",
|
||||
"Pull": "プル",
|
||||
"Pull model": "モデルをプル",
|
||||
|
||||
9
web/default/src/i18n/locales/ru.json
vendored
9
web/default/src/i18n/locales/ru.json
vendored
@ -1413,6 +1413,7 @@
|
||||
"Endpoint config": "Конфигурация конечной точки",
|
||||
"Endpoint Configuration": "Конфигурация конечной точки",
|
||||
"Endpoint Type": "Тип конечной точки",
|
||||
"Endpoint, provider-specific settings, and credentials.": "Эндпоинт, настройки провайдера и учетные данные.",
|
||||
"Endpoint:": "Конечная точка:",
|
||||
"Endpoints": "Конечные точки",
|
||||
"English": "Английский",
|
||||
@ -1716,15 +1717,15 @@
|
||||
"Filled {{count}} model(s)": "Заполнено {{count}} моделей",
|
||||
"Filled {{count}} related model(s)": "Заполнено {{count}} связанных моделей",
|
||||
"Filter": "Фильтр",
|
||||
"Filter by API key...": "Фильтр по API-ключу...",
|
||||
"Filter by channel ID": "Фильтр по ID канала",
|
||||
"Filter by group": "Фильтр по группе",
|
||||
"Filter by Midjourney task ID": "Фильтр по ID задачи Midjourney",
|
||||
"Filter by model name...": "Фильтр по имени модели...",
|
||||
"Filter by model...": "Фильтровать по модели...",
|
||||
"Filter by API key...": "Фильтр по API-ключу...",
|
||||
"Filter by name or ID...": "Фильтр по имени или ID...",
|
||||
"Filter by name...": "Фильтр по имени...",
|
||||
"Filter by name, ID, or key...": "Фильтровать по имени, ID или ключу...",
|
||||
"Filter by name...": "Фильтр по имени...",
|
||||
"Filter by price field": "Фильтр по полю цены",
|
||||
"Filter by ratio type": "Фильтровать по типу коэффициента",
|
||||
"Filter by request ID": "Фильтр по ID запроса",
|
||||
@ -1770,7 +1771,7 @@
|
||||
"footer.columns.related.links.oneApi": "Один API",
|
||||
"footer.columns.related.title": "Связанные проекты",
|
||||
"footer.defaultCopyright": "Все права защищены.",
|
||||
"footer.newapi.projectAttributionSuffix": "Все права защищены. Разработано участниками проекта.",
|
||||
"footer.new\u0061pi.projectAttributionSuffix": "Все права защищены. Разработано участниками проекта.",
|
||||
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Для каналов, добавленных после 10 мая 2025 г., не нужно удалять \".\" из имён моделей при развёртывании",
|
||||
"For private deployments, format: https://fastgpt.run/api/openapi": "Для частных развертываний, формат: https://fastgpt.run/api/openapi",
|
||||
"Force a syntactically valid JSON response": "Принудительно возвращать синтаксически корректный JSON",
|
||||
@ -2453,6 +2454,7 @@
|
||||
"Name Suffix": "Суффикс имени",
|
||||
"Name the channel and choose the upstream provider.": "Задайте имя канала и выберите upstream-провайдера.",
|
||||
"Name the channel, choose the provider, configure API access, and set credentials.": "Задайте имя канала, выберите провайдера, настройте доступ к API и учетные данные.",
|
||||
"Name, provider type, and availability.": "Название, тип провайдера и доступность.",
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "Собственный формат",
|
||||
"Need a redemption code?": "Нужен код активации?",
|
||||
@ -3102,6 +3104,7 @@
|
||||
"Public rankings page based on live usage data.": "Публичная страница рейтингов на основе реальных данных использования.",
|
||||
"Publish Date": "Дата публикации",
|
||||
"Published": "Опубликовано",
|
||||
"Published models, groups, and model remapping rules.": "Опубликованные модели, группы и правила переназначения моделей.",
|
||||
"Published:": "Опубликовано:",
|
||||
"Pull": "Загрузить",
|
||||
"Pull model": "Загрузить модель",
|
||||
|
||||
9
web/default/src/i18n/locales/vi.json
vendored
9
web/default/src/i18n/locales/vi.json
vendored
@ -1413,6 +1413,7 @@
|
||||
"Endpoint config": "Cấu hình điểm cuối",
|
||||
"Endpoint Configuration": "Cấu hình điểm cuối",
|
||||
"Endpoint Type": "Loại điểm cuối",
|
||||
"Endpoint, provider-specific settings, and credentials.": "Endpoint, cài đặt riêng của nhà cung cấp và thông tin xác thực.",
|
||||
"Endpoint:": "Điểm cuối:",
|
||||
"Endpoints": "Điểm cuối",
|
||||
"English": "Tiếng Anh",
|
||||
@ -1716,15 +1717,15 @@
|
||||
"Filled {{count}} model(s)": "Đã điền {{count}} mô hình",
|
||||
"Filled {{count}} related model(s)": "Đã điền {{count}} mô hình liên quan",
|
||||
"Filter": "Lọc",
|
||||
"Filter by API key...": "Lọc theo khóa API...",
|
||||
"Filter by channel ID": "Lọc theo ID kênh",
|
||||
"Filter by group": "Lọc theo nhóm",
|
||||
"Filter by Midjourney task ID": "Lọc theo ID nhiệm vụ Midjourney",
|
||||
"Filter by model name...": "Lọc theo tên mô hình...",
|
||||
"Filter by model...": "Lọc theo mẫu...",
|
||||
"Filter by API key...": "Lọc theo khóa API...",
|
||||
"Filter by name or ID...": "Lọc theo tên hoặc ID...",
|
||||
"Filter by name...": "Lọc theo tên...",
|
||||
"Filter by name, ID, or key...": "Lọc theo tên, ID hoặc khóa...",
|
||||
"Filter by name...": "Lọc theo tên...",
|
||||
"Filter by price field": "Lọc theo trường giá",
|
||||
"Filter by ratio type": "Lọc theo loại tỷ lệ",
|
||||
"Filter by request ID": "Lọc theo ID yêu cầu",
|
||||
@ -1770,7 +1771,7 @@
|
||||
"footer.columns.related.links.oneApi": "One API",
|
||||
"footer.columns.related.title": "Các Dự Án Liên Quan",
|
||||
"footer.defaultCopyright": "Bản quyền được bảo lưu.",
|
||||
"footer.newapi.projectAttributionSuffix": "Bản quyền được bảo lưu. Được thiết kế và phát triển bởi các cộng tác viên dự án.",
|
||||
"footer.new\u0061pi.projectAttributionSuffix": "Bản quyền được bảo lưu. Được thiết kế và phát triển bởi các cộng tác viên dự án.",
|
||||
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "Đối với các kênh được thêm sau ngày 10 tháng 5 năm 2025, không cần loại bỏ \".\" khỏi tên mô hình trong quá trình triển khai",
|
||||
"For private deployments, format: https://fastgpt.run/api/openapi": "Đối với các triển khai riêng tư, định dạng: https://fastgpt.run/api/openapi",
|
||||
"Force a syntactically valid JSON response": "Buộc phản hồi JSON hợp lệ về cú pháp",
|
||||
@ -2453,6 +2454,7 @@
|
||||
"Name Suffix": "Hậu tố tên",
|
||||
"Name the channel and choose the upstream provider.": "Đặt tên kênh và chọn nhà cung cấp upstream.",
|
||||
"Name the channel, choose the provider, configure API access, and set credentials.": "Đặt tên kênh, chọn nhà cung cấp, cấu hình truy cập API và thiết lập thông tin xác thực.",
|
||||
"Name, provider type, and availability.": "Tên, loại nhà cung cấp và trạng thái khả dụng.",
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "Định dạng gốc",
|
||||
"Need a redemption code?": "Cần mã đổi thưởng?",
|
||||
@ -3102,6 +3104,7 @@
|
||||
"Public rankings page based on live usage data.": "Trang bảng xếp hạng công khai dựa trên dữ liệu sử dụng thực.",
|
||||
"Publish Date": "Ngày xuất bản",
|
||||
"Published": "Đã xuất bản",
|
||||
"Published models, groups, and model remapping rules.": "Các mô hình đã xuất bản, nhóm và quy tắc ánh xạ lại mô hình.",
|
||||
"Published:": "Đã xuất bản:",
|
||||
"Pull": "Tải",
|
||||
"Pull model": "Tải mô hình",
|
||||
|
||||
9
web/default/src/i18n/locales/zh.json
vendored
9
web/default/src/i18n/locales/zh.json
vendored
@ -1413,6 +1413,7 @@
|
||||
"Endpoint config": "端点配置",
|
||||
"Endpoint Configuration": "端点配置",
|
||||
"Endpoint Type": "端点类型",
|
||||
"Endpoint, provider-specific settings, and credentials.": "接口地址、供应商专属设置和凭据。",
|
||||
"Endpoint:": "端点:",
|
||||
"Endpoints": "端点",
|
||||
"English": "英文",
|
||||
@ -1716,15 +1717,15 @@
|
||||
"Filled {{count}} model(s)": "已填充 {{count}} 个模型",
|
||||
"Filled {{count}} related model(s)": "已填充 {{count}} 个关联模型",
|
||||
"Filter": "筛选",
|
||||
"Filter by API key...": "按 API 密钥筛选...",
|
||||
"Filter by channel ID": "按通道 ID 筛选",
|
||||
"Filter by group": "按分组筛选",
|
||||
"Filter by Midjourney task ID": "按 Midjourney 任务 ID 筛选",
|
||||
"Filter by model name...": "按模型名称筛选...",
|
||||
"Filter by model...": "按模型筛选...",
|
||||
"Filter by API key...": "按 API 密钥筛选...",
|
||||
"Filter by name or ID...": "按名称或 ID 筛选...",
|
||||
"Filter by name...": "按名称筛选...",
|
||||
"Filter by name, ID, or key...": "按名称、ID 或密钥筛选...",
|
||||
"Filter by name...": "按名称筛选...",
|
||||
"Filter by price field": "按价格字段筛选",
|
||||
"Filter by ratio type": "按倍率类型筛选",
|
||||
"Filter by request ID": "按请求 ID 筛选",
|
||||
@ -1770,7 +1771,7 @@
|
||||
"footer.columns.related.links.oneApi": "One API",
|
||||
"footer.columns.related.title": "相关项目",
|
||||
"footer.defaultCopyright": "版权所有。",
|
||||
"footer.newapi.projectAttributionSuffix": "版权所有,由项目贡献者设计与开发。",
|
||||
"footer.new\u0061pi.projectAttributionSuffix": "版权所有,由项目贡献者设计与开发。",
|
||||
"For channels added after May 10, 2025, no need to remove \".\" from model names during deployment": "对于 2025 年 5 月 10 日之后添加的渠道,在部署时无需从模型名称中移除 \".\"",
|
||||
"For private deployments, format: https://fastgpt.run/api/openapi": "对于私有部署,格式为:https://fastgpt.run/api/openapi",
|
||||
"Force a syntactically valid JSON response": "强制返回语法合法的 JSON",
|
||||
@ -2453,6 +2454,7 @@
|
||||
"Name Suffix": "名称后缀",
|
||||
"Name the channel and choose the upstream provider.": "命名渠道并选择上游供应商。",
|
||||
"Name the channel, choose the provider, configure API access, and set credentials.": "命名渠道、选择供应商、配置 API 访问并设置凭据。",
|
||||
"Name, provider type, and availability.": "名称、供应商类型和可用状态。",
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "原生格式",
|
||||
"Need a redemption code?": "需要兑换码?",
|
||||
@ -3102,6 +3104,7 @@
|
||||
"Public rankings page based on live usage data.": "基于真实用量数据的公开排行榜页面。",
|
||||
"Publish Date": "发布日期",
|
||||
"Published": "已发布",
|
||||
"Published models, groups, and model remapping rules.": "已发布的模型、分组和模型重映射规则。",
|
||||
"Published:": "已发布:",
|
||||
"Pull": "拉取",
|
||||
"Pull model": "拉取模型",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user