mirror of
https://github.com/langgenius/dify.git
synced 2026-06-18 21:58:18 +08:00
Compare commits
2 Commits
main
...
codex/skip
| Author | SHA1 | Date | |
|---|---|---|---|
| 373d35c4bc | |||
| d2291417fa |
@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import PreviewContainer from '../container'
|
||||
|
||||
// Tests for PreviewContainer - a layout wrapper with header and scrollable main area
|
||||
// Tests for PreviewContainer - a layout wrapper with header and scrollable content area
|
||||
describe('PreviewContainer', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@ -17,11 +17,11 @@ describe('PreviewContainer', () => {
|
||||
expect(headerEl)!.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render children in a main element', () => {
|
||||
render(<PreviewContainer header="Header">Main content</PreviewContainer>)
|
||||
it('should render children in the content area', () => {
|
||||
render(<PreviewContainer header="Header" data-testid="inner-container">Main content</PreviewContainer>)
|
||||
|
||||
const mainEl = screen.getByRole('main')
|
||||
expect(mainEl)!.toHaveTextContent('Main content')
|
||||
const contentEl = screen.getByTestId('inner-container').lastElementChild
|
||||
expect(contentEl)!.toHaveTextContent('Main content')
|
||||
})
|
||||
|
||||
it('should render both header and children simultaneously', () => {
|
||||
@ -36,10 +36,11 @@ describe('PreviewContainer', () => {
|
||||
})
|
||||
|
||||
it('should render without children', () => {
|
||||
render(<PreviewContainer header="Header" />)
|
||||
render(<PreviewContainer header="Header" data-testid="inner-container" />)
|
||||
|
||||
expect(screen.getByRole('main'))!.toBeInTheDocument()
|
||||
expect(screen.getByRole('main').childElementCount).toBe(0)
|
||||
const contentEl = screen.getByTestId('inner-container').lastElementChild
|
||||
expect(contentEl)!.toBeInTheDocument()
|
||||
expect(contentEl!.childElementCount).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
@ -52,16 +53,14 @@ describe('PreviewContainer', () => {
|
||||
expect(container.firstElementChild)!.toHaveClass('outer-class')
|
||||
})
|
||||
|
||||
it('should apply mainClassName to the main element', () => {
|
||||
it('should apply mainClassName to the content area', () => {
|
||||
render(
|
||||
<PreviewContainer header="Header" mainClassName="custom-main">Content</PreviewContainer>,
|
||||
<PreviewContainer header="Header" mainClassName="custom-main" data-testid="inner-container">Content</PreviewContainer>,
|
||||
)
|
||||
|
||||
const mainEl = screen.getByRole('main')
|
||||
expect(mainEl)!.toHaveClass('custom-main')
|
||||
// Default classes should still be present
|
||||
// Default classes should still be present
|
||||
expect(mainEl)!.toHaveClass('w-full', 'grow', 'overflow-y-auto', 'px-6', 'py-5')
|
||||
const contentEl = screen.getByTestId('inner-container').lastElementChild
|
||||
expect(contentEl)!.toHaveClass('custom-main')
|
||||
expect(contentEl)!.toHaveClass('w-full', 'grow', 'overflow-y-auto', 'px-6', 'py-5')
|
||||
})
|
||||
|
||||
it('should forward ref to the inner container div', () => {
|
||||
@ -116,10 +115,10 @@ describe('PreviewContainer', () => {
|
||||
expect(inner)!.toHaveClass('flex', 'h-full', 'w-full', 'flex-col')
|
||||
})
|
||||
|
||||
it('should have main with overflow-y-auto for scrolling', () => {
|
||||
render(<PreviewContainer header="Header">Content</PreviewContainer>)
|
||||
it('should have content area with overflow-y-auto for scrolling', () => {
|
||||
render(<PreviewContainer header="Header" data-testid="inner-container">Content</PreviewContainer>)
|
||||
|
||||
expect(screen.getByRole('main'))!.toHaveClass('overflow-y-auto')
|
||||
expect(screen.getByTestId('inner-container').lastElementChild)!.toHaveClass('overflow-y-auto')
|
||||
})
|
||||
})
|
||||
|
||||
@ -139,9 +138,9 @@ describe('PreviewContainer', () => {
|
||||
})
|
||||
|
||||
it('should render with null children', () => {
|
||||
render(<PreviewContainer header="Header">{null}</PreviewContainer>)
|
||||
render(<PreviewContainer header="Header" data-testid="inner-container">{null}</PreviewContainer>)
|
||||
|
||||
expect(screen.getByRole('main'))!.toBeInTheDocument()
|
||||
expect(screen.getByTestId('inner-container').lastElementChild)!.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render with multiple children', () => {
|
||||
|
||||
@ -19,9 +19,9 @@ const PreviewContainer: FC<PreviewContainerProps> = (props) => {
|
||||
<header className="border-b border-divider-subtle pt-4 pr-4 pb-3 pl-5">
|
||||
{header}
|
||||
</header>
|
||||
<main className={cn('w-full grow overflow-y-auto px-6 py-5', mainClassName)}>
|
||||
<div className={cn('w-full grow overflow-y-auto px-6 py-5', mainClassName)}>
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import MainNavLayout from '../layout'
|
||||
|
||||
vi.mock('@/app/components/header', () => ({
|
||||
@ -10,6 +10,12 @@ vi.mock('@/app/components/header/header-wrapper', () => ({
|
||||
default: ({ children }: { children: ReactNode }) => <div data-testid="header-wrapper">{children}</div>,
|
||||
}))
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('../index', () => ({
|
||||
default: ({ className }: { className?: string }) => <aside className={className} data-testid="main-nav">MainNav</aside>,
|
||||
}))
|
||||
@ -34,4 +40,37 @@ describe('MainNavLayout', () => {
|
||||
expect(screen.queryByTestId('header-wrapper')).not.toBeInTheDocument()
|
||||
expect(screen.queryByTestId('desktop-header')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders one main landmark as the skip navigation target', () => {
|
||||
render(<MainNavLayout><div>content</div></MainNavLayout>)
|
||||
|
||||
const main = screen.getByRole('main')
|
||||
|
||||
expect(screen.getAllByRole('main')).toHaveLength(1)
|
||||
expect(main).toHaveAttribute('id', 'main-content')
|
||||
expect(main).toHaveAttribute('tabIndex', '-1')
|
||||
expect(main).toHaveClass('outline-hidden', 'focus:outline-hidden', 'focus-visible:outline-hidden')
|
||||
expect(main).toHaveTextContent('content')
|
||||
})
|
||||
|
||||
it('renders skip navigation before the repeated main navigation', () => {
|
||||
const { container } = render(<MainNavLayout><div>content</div></MainNavLayout>)
|
||||
|
||||
const skipLink = screen.getByRole('link', { name: 'navigation.skipToMain' })
|
||||
|
||||
expect(skipLink).toHaveAttribute('href', '#main-content')
|
||||
expect(skipLink).toHaveClass('outline-hidden', 'focus-visible:ring-2', 'focus-visible:ring-state-accent-solid')
|
||||
expect(container.querySelector('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])')).toBe(skipLink)
|
||||
})
|
||||
|
||||
it('moves focus to the main content when skip navigation is activated', () => {
|
||||
render(<MainNavLayout><div>content</div></MainNavLayout>)
|
||||
|
||||
const skipLink = screen.getByRole('link', { name: 'navigation.skipToMain' })
|
||||
const main = screen.getByRole('main')
|
||||
|
||||
fireEvent.click(skipLink)
|
||||
|
||||
expect(main).toHaveFocus()
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import MainNav from './index'
|
||||
import { MAIN_CONTENT_ID, SkipNav } from './skip-nav'
|
||||
|
||||
type MainNavLayoutProps = {
|
||||
children: ReactNode
|
||||
@ -10,12 +12,19 @@ type MainNavLayoutProps = {
|
||||
const MainNavLayout = ({
|
||||
children,
|
||||
}: MainNavLayoutProps) => {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
return (
|
||||
<div className="flex h-0 min-h-0 grow overflow-hidden bg-background-body">
|
||||
<SkipNav>{t('navigation.skipToMain')}</SkipNav>
|
||||
<MainNav />
|
||||
<div className="flex min-w-0 grow flex-col overflow-hidden">
|
||||
<main
|
||||
id={MAIN_CONTENT_ID}
|
||||
tabIndex={-1}
|
||||
className="flex min-w-0 grow flex-col overflow-hidden outline-hidden focus:outline-hidden focus-visible:outline-hidden"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
37
web/app/components/main-nav/skip-nav.tsx
Normal file
37
web/app/components/main-nav/skip-nav.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
'use client'
|
||||
|
||||
import type { ComponentProps, MouseEventHandler } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
|
||||
export const MAIN_CONTENT_ID = 'main-content'
|
||||
const MAIN_CONTENT_HREF = `#${MAIN_CONTENT_ID}`
|
||||
|
||||
export function SkipNav({
|
||||
className,
|
||||
children,
|
||||
onClick,
|
||||
...props
|
||||
}: ComponentProps<'a'>) {
|
||||
const handleClick: MouseEventHandler<HTMLAnchorElement> = (event) => {
|
||||
onClick?.(event)
|
||||
|
||||
if (event.defaultPrevented)
|
||||
return
|
||||
|
||||
document.getElementById(MAIN_CONTENT_ID)?.focus()
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
href={MAIN_CONTENT_HREF}
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
'fixed top-2 left-2 z-60 inline-flex h-9 -translate-y-[calc(100%+0.75rem)] items-center justify-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 system-sm-medium text-components-button-secondary-text shadow-lg shadow-shadow-shadow-5 outline-hidden transition-transform duration-150 focus-visible:translate-y-0 focus-visible:ring-2 focus-visible:ring-state-accent-solid motion-reduce:transition-none',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
@ -236,7 +236,7 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
|
||||
<SearchInput placeholder={t('nodes.agent.strategy.searchPlaceholder', { ns: 'workflow' })} value={query} onValueChange={setQuery} className="w-full" />
|
||||
<ViewTypeSelect viewType={viewType} onChange={setViewType} />
|
||||
</header>
|
||||
<main className="relative flex w-full flex-col overflow-hidden md:max-h-[300px] xl:max-h-[400px] 2xl:max-h-[564px]" ref={wrapElemRef}>
|
||||
<div className="relative flex w-full flex-col overflow-hidden md:max-h-[300px] xl:max-h-[400px] 2xl:max-h-[564px]" ref={wrapElemRef}>
|
||||
<Tools
|
||||
tools={filteredTools}
|
||||
viewType={viewType}
|
||||
@ -268,7 +268,7 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
|
||||
disableMaxWidth
|
||||
/>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@ -27,10 +27,10 @@ export function AgentDetailLayout({
|
||||
useDocumentTitle(agentQuery.data?.name ?? t('agentDetail.documentTitle'))
|
||||
|
||||
return (
|
||||
<main className="relative flex h-full min-w-0 flex-col overflow-hidden">
|
||||
<div className="relative flex h-full min-w-0 flex-col overflow-hidden">
|
||||
<div className="min-h-0 min-w-0 flex-1 overflow-auto">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ export default function RosterPage() {
|
||||
useDocumentTitle(tCommon('menus.roster'))
|
||||
|
||||
return (
|
||||
<main className="flex h-0 min-w-0 grow flex-col overflow-hidden bg-background-body">
|
||||
<div className="flex h-0 min-w-0 grow flex-col overflow-hidden bg-background-body">
|
||||
<Tabs defaultValue="agent" className="flex min-h-0 flex-1 flex-col">
|
||||
<div className="h-25.5 shrink-0 bg-background-body px-8 pt-4 pb-4">
|
||||
<div className="flex min-w-0 items-center justify-between gap-4">
|
||||
@ -145,6 +145,6 @@ export default function RosterPage() {
|
||||
</ScrollAreaRoot>
|
||||
</TabsPanel>
|
||||
</Tabs>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ export function CreateDeploymentGuide() {
|
||||
backdropClassName="bg-background-overlay-backdrop backdrop-blur-[6px]"
|
||||
className="top-4 bottom-4 h-auto max-h-none w-[min(calc(100vw-2rem),1120px)] max-w-none translate-y-0 overflow-hidden border-effects-highlight bg-background-default-subtle p-0"
|
||||
>
|
||||
<main className="relative flex h-full min-w-0 grow flex-col overflow-hidden">
|
||||
<div className="relative flex h-full min-w-0 grow flex-col overflow-hidden">
|
||||
<Link
|
||||
href="/deployments"
|
||||
aria-label={t('createGuide.nav.back')}
|
||||
@ -28,7 +28,7 @@ export function CreateDeploymentGuide() {
|
||||
<CreateDeploymentGuideProvider>
|
||||
<CreateDeploymentGuideShell />
|
||||
</CreateDeploymentGuideProvider>
|
||||
</main>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "نموذج تحويل النص إلى كلام",
|
||||
"modelProvider.ttsModel.tip": "تعيين النموذج الافتراضي لإدخال تحويل النص إلى كلام في المحادثة.",
|
||||
"modelProvider.upgradeForLoadBalancing": "قم بترقية خطتك لتمكين موازنة التحميل.",
|
||||
"navigation.skipToMain": "الانتقال إلى المحتوى الرئيسي",
|
||||
"noData": "لا توجد بيانات",
|
||||
"operation.add": "إضافة",
|
||||
"operation.added": "تمت الإضافة",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Text-zu-Sprache-Modell",
|
||||
"modelProvider.ttsModel.tip": "Legen Sie das Standardmodell für die Text-zu-Sprache-Eingabe in Konversationen fest.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Aktualisieren Sie Ihren Plan, um den Lastenausgleich zu aktivieren.",
|
||||
"navigation.skipToMain": "Zum Hauptinhalt springen",
|
||||
"noData": "Keine Daten",
|
||||
"operation.add": "Hinzufügen",
|
||||
"operation.added": "Hinzugefügt",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Text-to-Speech Model",
|
||||
"modelProvider.ttsModel.tip": "Set the default model for text-to-speech input in conversation.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Upgrade your plan to enable Load Balancing.",
|
||||
"navigation.skipToMain": "Skip to main content",
|
||||
"noData": "No data",
|
||||
"operation.add": "Add",
|
||||
"operation.added": "Added",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Modelo de Texto a Voz",
|
||||
"modelProvider.ttsModel.tip": "Establece el modelo predeterminado para la entrada de texto a voz en la conversación.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Actualiza tu plan para habilitar el Balanceo de Carga.",
|
||||
"navigation.skipToMain": "Saltar al contenido principal",
|
||||
"noData": "Sin datos",
|
||||
"operation.add": "Agregar",
|
||||
"operation.added": "Agregado",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "مدل تبدیل متن به گفتار",
|
||||
"modelProvider.ttsModel.tip": "مدل پیشفرض را برای ورودی متن به گفتار در مکالمه تنظیم کنید.",
|
||||
"modelProvider.upgradeForLoadBalancing": "برای فعال کردن تعادل بار، طرح خود را ارتقا دهید.",
|
||||
"navigation.skipToMain": "پرش به محتوای اصلی",
|
||||
"noData": "بدون داده",
|
||||
"operation.add": "افزودن",
|
||||
"operation.added": "اضافه شد",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Modèle de Texte-à-Parole",
|
||||
"modelProvider.ttsModel.tip": "Définissez le modèle par défaut pour l'entrée de texte à la parole dans une conversation.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Mettez à niveau votre plan pour activer l’équilibrage de charge.",
|
||||
"navigation.skipToMain": "Aller au contenu principal",
|
||||
"noData": "Aucune donnée",
|
||||
"operation.add": "Ajouter",
|
||||
"operation.added": "Ajouté",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "पाठ-से-भाषण मॉडल",
|
||||
"modelProvider.ttsModel.tip": "संवाद में पाठ-से-भाषण इनपुट के लिए डिफ़ॉल्ट मॉडल सेट करें।",
|
||||
"modelProvider.upgradeForLoadBalancing": "लोड बैलेंसिंग सक्षम करने के लिए अपनी योजना अपग्रेड करें।",
|
||||
"navigation.skipToMain": "मुख्य सामग्री पर जाएं",
|
||||
"noData": "कोई डेटा नहीं",
|
||||
"operation.add": "जोड़ें",
|
||||
"operation.added": "जोड़ा गया",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Model Teks-ke-Ucapan",
|
||||
"modelProvider.ttsModel.tip": "Atur model default untuk input teks-ke-ucapan dalam percakapan.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Tingkatkan paket Anda untuk mengaktifkan Penyeimbangan Beban.",
|
||||
"navigation.skipToMain": "Lewati ke konten utama",
|
||||
"noData": "Tidak ada data",
|
||||
"operation.add": "Tambah",
|
||||
"operation.added": "Ditambahkan",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Modello da Testo a Voce",
|
||||
"modelProvider.ttsModel.tip": "Imposta il modello predefinito per l'input da testo a voce nella conversazione.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Aggiorna il tuo piano per abilitare il Bilanciamento del Carico.",
|
||||
"navigation.skipToMain": "Vai al contenuto principale",
|
||||
"noData": "Nessun dato",
|
||||
"operation.add": "Aggiungi",
|
||||
"operation.added": "Aggiunto",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "テキスト-to-音声モデル",
|
||||
"modelProvider.ttsModel.tip": "会話でのテキスト-to-音声入力に使用するデフォルトモデルを設定します。",
|
||||
"modelProvider.upgradeForLoadBalancing": "負荷分散を利用するには、プランのアップグレードが必要です。",
|
||||
"navigation.skipToMain": "メインコンテンツへスキップ",
|
||||
"noData": "データなし",
|
||||
"operation.add": "追加",
|
||||
"operation.added": "追加済み",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "텍스트-to-음성 모델",
|
||||
"modelProvider.ttsModel.tip": "대화에서의 텍스트-to-음성 입력에 사용되는 기본 모델을 설정합니다.",
|
||||
"modelProvider.upgradeForLoadBalancing": "로드 밸런싱을 사용하도록 계획을 업그레이드합니다.",
|
||||
"navigation.skipToMain": "본문으로 건너뛰기",
|
||||
"noData": "데이터 없음",
|
||||
"operation.add": "추가",
|
||||
"operation.added": "추가됨",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Text-to-Speech Model",
|
||||
"modelProvider.ttsModel.tip": "Set the default model for text-to-speech input in conversation.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Upgrade your plan to enable Load Balancing.",
|
||||
"navigation.skipToMain": "Naar hoofdinhoud springen",
|
||||
"noData": "No data",
|
||||
"operation.add": "Add",
|
||||
"operation.added": "Added",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Model tekstu na mowę",
|
||||
"modelProvider.ttsModel.tip": "Ustaw domyślny model dla konwersji tekstu na mowę w rozmowach.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Uaktualnij swój plan, aby włączyć równoważenie obciążenia.",
|
||||
"navigation.skipToMain": "Przejdź do głównej treści",
|
||||
"noData": "Brak danych",
|
||||
"operation.add": "Dodaj",
|
||||
"operation.added": "Dodano",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Modelo de Texto para Fala",
|
||||
"modelProvider.ttsModel.tip": "Defina o modelo padrão para entrada de texto para fala na conversa.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Atualize seu plano para habilitar o balanceamento de carga.",
|
||||
"navigation.skipToMain": "Pular para o conteúdo principal",
|
||||
"noData": "Sem dados",
|
||||
"operation.add": "Adicionar",
|
||||
"operation.added": "Adicionado",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Model de conversie vorbire-la-text",
|
||||
"modelProvider.ttsModel.tip": "Setați modelul implicit pentru intrarea de conversie vorbire-la-text în conversație.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Actualizați-vă planul pentru a activa Load Balancing.",
|
||||
"navigation.skipToMain": "Sări la conținutul principal",
|
||||
"noData": "Fără date",
|
||||
"operation.add": "Adaugă",
|
||||
"operation.added": "Adăugat",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Модель преобразования текста в речь",
|
||||
"modelProvider.ttsModel.tip": "Установите модель по умолчанию для ввода текста в речь в разговоре.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Обновите свой тарифный план, чтобы включить балансировку нагрузки.",
|
||||
"navigation.skipToMain": "Перейти к основному содержанию",
|
||||
"noData": "Нет данных",
|
||||
"operation.add": "Добавить",
|
||||
"operation.added": "Добавлено",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Model za pretvorbo besedila v govor",
|
||||
"modelProvider.ttsModel.tip": "Nastavite privzeti model za pretvorbo besedila v govor v pogovoru.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Nadgradite svoj načrt, da omogočite uravnoteženje obremenitev.",
|
||||
"navigation.skipToMain": "Preskoči na glavno vsebino",
|
||||
"noData": "Ni podatkov",
|
||||
"operation.add": "Dodaj",
|
||||
"operation.added": "Dodano",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "โมเดลการแปลงข้อความเป็นคําพูด",
|
||||
"modelProvider.ttsModel.tip": "ตั้งค่าโมเดลเริ่มต้นสําหรับการป้อนข้อมูลเป็นข้อความเป็นคําพูดในการสนทนา",
|
||||
"modelProvider.upgradeForLoadBalancing": "อัปเกรดแผนของคุณเพื่อเปิดใช้งานการปรับสมดุลโหลด",
|
||||
"navigation.skipToMain": "ข้ามไปยังเนื้อหาหลัก",
|
||||
"noData": "ไม่มีข้อมูล",
|
||||
"operation.add": "เพิ่ม",
|
||||
"operation.added": "เพิ่ม",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Metinden Konuşmaya Modeli",
|
||||
"modelProvider.ttsModel.tip": "Konuşmada metinden konuşmaya giriş için varsayılan modeli ayarlayın.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Yük Dengelemeyi etkinleştirmek için planınızı yükseltin.",
|
||||
"navigation.skipToMain": "Ana içeriğe geç",
|
||||
"noData": "Veri yok",
|
||||
"operation.add": "Ekle",
|
||||
"operation.added": "Eklendi",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Модель перетворення тексту в мовлення",
|
||||
"modelProvider.ttsModel.tip": "Встановіть модель за замовчуванням для введення тексту в мовлення в розмові.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Оновіть свій план, щоб увімкнути балансування навантаження.",
|
||||
"navigation.skipToMain": "Перейти до основного вмісту",
|
||||
"noData": "Немає даних",
|
||||
"operation.add": "Додати",
|
||||
"operation.added": "Додано",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "Mô hình Văn bản thành Tiếng nói",
|
||||
"modelProvider.ttsModel.tip": "Thiết lập mô hình mặc định cho đầu vào văn bản thành tiếng nói trong cuộc trò chuyện.",
|
||||
"modelProvider.upgradeForLoadBalancing": "Nâng cấp gói của bạn để bật Cân bằng tải.",
|
||||
"navigation.skipToMain": "Chuyển đến nội dung chính",
|
||||
"noData": "Không có dữ liệu",
|
||||
"operation.add": "Thêm",
|
||||
"operation.added": "Đã thêm",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "文本转语音模型",
|
||||
"modelProvider.ttsModel.tip": "设置对话中文字转语音输出的默认使用模型。",
|
||||
"modelProvider.upgradeForLoadBalancing": "升级以解锁负载均衡功能",
|
||||
"navigation.skipToMain": "跳转到主要内容",
|
||||
"noData": "暂无数据",
|
||||
"operation.add": "添加",
|
||||
"operation.added": "已添加",
|
||||
|
||||
@ -521,6 +521,7 @@
|
||||
"modelProvider.ttsModel.key": "文字轉語音模型",
|
||||
"modelProvider.ttsModel.tip": "設定對話中文字轉語音輸出的預設使用模型。",
|
||||
"modelProvider.upgradeForLoadBalancing": "升級您的計劃以啟用 Load Balancing。",
|
||||
"navigation.skipToMain": "跳至主要內容",
|
||||
"noData": "無資料",
|
||||
"operation.add": "新增",
|
||||
"operation.added": "已新增",
|
||||
|
||||
Reference in New Issue
Block a user