diff --git a/web/app/components/datasets/preview/__tests__/container.spec.tsx b/web/app/components/datasets/preview/__tests__/container.spec.tsx index 473d91f8574..9f7ff17522a 100644 --- a/web/app/components/datasets/preview/__tests__/container.spec.tsx +++ b/web/app/components/datasets/preview/__tests__/container.spec.tsx @@ -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(Main content) + it('should render children in the content area', () => { + render(Main content) - 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() + render() - 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( - Content, + Content, ) - 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(Content) + it('should have content area with overflow-y-auto for scrolling', () => { + render(Content) - 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({null}) + render({null}) - expect(screen.getByRole('main'))!.toBeInTheDocument() + expect(screen.getByTestId('inner-container').lastElementChild)!.toBeInTheDocument() }) it('should render with multiple children', () => { diff --git a/web/app/components/datasets/preview/container.tsx b/web/app/components/datasets/preview/container.tsx index 742c1e2ac15..c651de6c1f7 100644 --- a/web/app/components/datasets/preview/container.tsx +++ b/web/app/components/datasets/preview/container.tsx @@ -19,9 +19,9 @@ const PreviewContainer: FC = (props) => {
{header}
-
+
{children} -
+ ) diff --git a/web/app/components/main-nav/__tests__/layout.spec.tsx b/web/app/components/main-nav/__tests__/layout.spec.tsx index 0157235b6f2..02d2da91c90 100644 --- a/web/app/components/main-nav/__tests__/layout.spec.tsx +++ b/web/app/components/main-nav/__tests__/layout.spec.tsx @@ -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 }) =>
{children}
, })) +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), +})) + vi.mock('../index', () => ({ default: ({ className }: { className?: string }) => , })) @@ -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(
content
) + + 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(
content
) + + 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(
content
) + + const skipLink = screen.getByRole('link', { name: 'navigation.skipToMain' }) + const main = screen.getByRole('main') + + fireEvent.click(skipLink) + + expect(main).toHaveFocus() + }) }) diff --git a/web/app/components/main-nav/layout.tsx b/web/app/components/main-nav/layout.tsx index d01af10f271..44730fe7468 100644 --- a/web/app/components/main-nav/layout.tsx +++ b/web/app/components/main-nav/layout.tsx @@ -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 (
+ {t('navigation.skipToMain')} -
+
{children} -
+
) } diff --git a/web/app/components/main-nav/skip-nav.tsx b/web/app/components/main-nav/skip-nav.tsx new file mode 100644 index 00000000000..eda7ad4b5f0 --- /dev/null +++ b/web/app/components/main-nav/skip-nav.tsx @@ -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 = (event) => { + onClick?.(event) + + if (event.defaultPrevented) + return + + document.getElementById(MAIN_CONTENT_ID)?.focus() + } + + return ( + + {children} + + ) +} diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index 9ef2dafd8e5..b432213061b 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -236,7 +236,7 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => -
+
disableMaxWidth /> )} -
+ diff --git a/web/features/agent-v2/agent-detail/layout.tsx b/web/features/agent-v2/agent-detail/layout.tsx index 863a30e24fb..7088732d9b0 100644 --- a/web/features/agent-v2/agent-detail/layout.tsx +++ b/web/features/agent-v2/agent-detail/layout.tsx @@ -27,10 +27,10 @@ export function AgentDetailLayout({ useDocumentTitle(agentQuery.data?.name ?? t('agentDetail.documentTitle')) return ( -
+
{children}
-
+ ) } diff --git a/web/features/agent-v2/roster/page.tsx b/web/features/agent-v2/roster/page.tsx index c4f75470df6..2527c0875b5 100644 --- a/web/features/agent-v2/roster/page.tsx +++ b/web/features/agent-v2/roster/page.tsx @@ -84,7 +84,7 @@ export default function RosterPage() { useDocumentTitle(tCommon('menus.roster')) return ( -
+
@@ -145,6 +145,6 @@ export default function RosterPage() { -
+ ) } diff --git a/web/features/deployments/create-guide/index.tsx b/web/features/deployments/create-guide/index.tsx index 462b869e7e5..a1f71716c91 100644 --- a/web/features/deployments/create-guide/index.tsx +++ b/web/features/deployments/create-guide/index.tsx @@ -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" > -
+
-
+ ) diff --git a/web/i18n/ar-TN/common.json b/web/i18n/ar-TN/common.json index f28682d7171..350d3b10788 100644 --- a/web/i18n/ar-TN/common.json +++ b/web/i18n/ar-TN/common.json @@ -516,6 +516,7 @@ "modelProvider.ttsModel.key": "نموذج تحويل النص إلى كلام", "modelProvider.ttsModel.tip": "تعيين النموذج الافتراضي لإدخال تحويل النص إلى كلام في المحادثة.", "modelProvider.upgradeForLoadBalancing": "قم بترقية خطتك لتمكين موازنة التحميل.", + "navigation.skipToMain": "الانتقال إلى المحتوى الرئيسي", "noData": "لا توجد بيانات", "operation.add": "إضافة", "operation.added": "تمت الإضافة", diff --git a/web/i18n/de-DE/common.json b/web/i18n/de-DE/common.json index c4744db342e..84c57d8523c 100644 --- a/web/i18n/de-DE/common.json +++ b/web/i18n/de-DE/common.json @@ -516,6 +516,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", diff --git a/web/i18n/en-US/common.json b/web/i18n/en-US/common.json index a58816d826a..ded6c07e1dc 100644 --- a/web/i18n/en-US/common.json +++ b/web/i18n/en-US/common.json @@ -545,6 +545,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", diff --git a/web/i18n/es-ES/common.json b/web/i18n/es-ES/common.json index f915fd3faf2..6ce03dc555e 100644 --- a/web/i18n/es-ES/common.json +++ b/web/i18n/es-ES/common.json @@ -516,6 +516,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", diff --git a/web/i18n/fa-IR/common.json b/web/i18n/fa-IR/common.json index 10f74e2afc4..5ed801fe2a2 100644 --- a/web/i18n/fa-IR/common.json +++ b/web/i18n/fa-IR/common.json @@ -516,6 +516,7 @@ "modelProvider.ttsModel.key": "مدل تبدیل متن به گفتار", "modelProvider.ttsModel.tip": "مدل پیش‌فرض را برای ورودی متن به گفتار در مکالمه تنظیم کنید.", "modelProvider.upgradeForLoadBalancing": "برای فعال کردن تعادل بار، طرح خود را ارتقا دهید.", + "navigation.skipToMain": "پرش به محتوای اصلی", "noData": "بدون داده", "operation.add": "افزودن", "operation.added": "اضافه شد", diff --git a/web/i18n/fr-FR/common.json b/web/i18n/fr-FR/common.json index 9f1a640874d..bec097fe0f7 100644 --- a/web/i18n/fr-FR/common.json +++ b/web/i18n/fr-FR/common.json @@ -516,6 +516,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é", diff --git a/web/i18n/hi-IN/common.json b/web/i18n/hi-IN/common.json index a254bee5b07..d85b715e1cc 100644 --- a/web/i18n/hi-IN/common.json +++ b/web/i18n/hi-IN/common.json @@ -516,6 +516,7 @@ "modelProvider.ttsModel.key": "पाठ-से-भाषण मॉडल", "modelProvider.ttsModel.tip": "संवाद में पाठ-से-भाषण इनपुट के लिए डिफ़ॉल्ट मॉडल सेट करें।", "modelProvider.upgradeForLoadBalancing": "लोड बैलेंसिंग सक्षम करने के लिए अपनी योजना अपग्रेड करें।", + "navigation.skipToMain": "मुख्य सामग्री पर जाएं", "noData": "कोई डेटा नहीं", "operation.add": "जोड़ें", "operation.added": "जोड़ा गया", diff --git a/web/i18n/id-ID/common.json b/web/i18n/id-ID/common.json index 8b5a1dc5a39..c11b0233b8f 100644 --- a/web/i18n/id-ID/common.json +++ b/web/i18n/id-ID/common.json @@ -516,6 +516,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", diff --git a/web/i18n/it-IT/common.json b/web/i18n/it-IT/common.json index 6e3b1454ae8..8513ffd7c3d 100644 --- a/web/i18n/it-IT/common.json +++ b/web/i18n/it-IT/common.json @@ -516,6 +516,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", diff --git a/web/i18n/ja-JP/common.json b/web/i18n/ja-JP/common.json index 7b8b37a8f56..9922049861a 100644 --- a/web/i18n/ja-JP/common.json +++ b/web/i18n/ja-JP/common.json @@ -544,6 +544,7 @@ "modelProvider.ttsModel.key": "テキスト-to-音声モデル", "modelProvider.ttsModel.tip": "会話でのテキスト-to-音声入力に使用するデフォルトモデルを設定します。", "modelProvider.upgradeForLoadBalancing": "負荷分散を利用するには、プランのアップグレードが必要です。", + "navigation.skipToMain": "メインコンテンツへスキップ", "noData": "データなし", "operation.add": "追加", "operation.added": "追加済み", diff --git a/web/i18n/ko-KR/common.json b/web/i18n/ko-KR/common.json index 64db784d138..88a95f576d5 100644 --- a/web/i18n/ko-KR/common.json +++ b/web/i18n/ko-KR/common.json @@ -516,6 +516,7 @@ "modelProvider.ttsModel.key": "텍스트-to-음성 모델", "modelProvider.ttsModel.tip": "대화에서의 텍스트-to-음성 입력에 사용되는 기본 모델을 설정합니다.", "modelProvider.upgradeForLoadBalancing": "로드 밸런싱을 사용하도록 계획을 업그레이드합니다.", + "navigation.skipToMain": "본문으로 건너뛰기", "noData": "데이터 없음", "operation.add": "추가", "operation.added": "추가됨", diff --git a/web/i18n/nl-NL/common.json b/web/i18n/nl-NL/common.json index 8c03f8fd4d2..48ac771291a 100644 --- a/web/i18n/nl-NL/common.json +++ b/web/i18n/nl-NL/common.json @@ -516,6 +516,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", diff --git a/web/i18n/pl-PL/common.json b/web/i18n/pl-PL/common.json index b156c28448f..cd22faf232c 100644 --- a/web/i18n/pl-PL/common.json +++ b/web/i18n/pl-PL/common.json @@ -516,6 +516,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", diff --git a/web/i18n/pt-BR/common.json b/web/i18n/pt-BR/common.json index ce04750e3c5..5ff10fe8629 100644 --- a/web/i18n/pt-BR/common.json +++ b/web/i18n/pt-BR/common.json @@ -516,6 +516,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", diff --git a/web/i18n/ro-RO/common.json b/web/i18n/ro-RO/common.json index 50216e7108b..9f2c4269b91 100644 --- a/web/i18n/ro-RO/common.json +++ b/web/i18n/ro-RO/common.json @@ -516,6 +516,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", diff --git a/web/i18n/ru-RU/common.json b/web/i18n/ru-RU/common.json index 23b8625b756..c102589aa8e 100644 --- a/web/i18n/ru-RU/common.json +++ b/web/i18n/ru-RU/common.json @@ -516,6 +516,7 @@ "modelProvider.ttsModel.key": "Модель преобразования текста в речь", "modelProvider.ttsModel.tip": "Установите модель по умолчанию для ввода текста в речь в разговоре.", "modelProvider.upgradeForLoadBalancing": "Обновите свой тарифный план, чтобы включить балансировку нагрузки.", + "navigation.skipToMain": "Перейти к основному содержанию", "noData": "Нет данных", "operation.add": "Добавить", "operation.added": "Добавлено", diff --git a/web/i18n/sl-SI/common.json b/web/i18n/sl-SI/common.json index b1a1726d195..f0bf2b62e9c 100644 --- a/web/i18n/sl-SI/common.json +++ b/web/i18n/sl-SI/common.json @@ -516,6 +516,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", diff --git a/web/i18n/th-TH/common.json b/web/i18n/th-TH/common.json index d05b8b38a08..89776e7e116 100644 --- a/web/i18n/th-TH/common.json +++ b/web/i18n/th-TH/common.json @@ -516,6 +516,7 @@ "modelProvider.ttsModel.key": "โมเดลการแปลงข้อความเป็นคําพูด", "modelProvider.ttsModel.tip": "ตั้งค่าโมเดลเริ่มต้นสําหรับการป้อนข้อมูลเป็นข้อความเป็นคําพูดในการสนทนา", "modelProvider.upgradeForLoadBalancing": "อัปเกรดแผนของคุณเพื่อเปิดใช้งานการปรับสมดุลโหลด", + "navigation.skipToMain": "ข้ามไปยังเนื้อหาหลัก", "noData": "ไม่มีข้อมูล", "operation.add": "เพิ่ม", "operation.added": "เพิ่ม", diff --git a/web/i18n/tr-TR/common.json b/web/i18n/tr-TR/common.json index 5b0fb47bdb8..e629e783b23 100644 --- a/web/i18n/tr-TR/common.json +++ b/web/i18n/tr-TR/common.json @@ -516,6 +516,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", diff --git a/web/i18n/uk-UA/common.json b/web/i18n/uk-UA/common.json index 9c10c3a9610..c9876452149 100644 --- a/web/i18n/uk-UA/common.json +++ b/web/i18n/uk-UA/common.json @@ -516,6 +516,7 @@ "modelProvider.ttsModel.key": "Модель перетворення тексту в мовлення", "modelProvider.ttsModel.tip": "Встановіть модель за замовчуванням для введення тексту в мовлення в розмові.", "modelProvider.upgradeForLoadBalancing": "Оновіть свій план, щоб увімкнути балансування навантаження.", + "navigation.skipToMain": "Перейти до основного вмісту", "noData": "Немає даних", "operation.add": "Додати", "operation.added": "Додано", diff --git a/web/i18n/vi-VN/common.json b/web/i18n/vi-VN/common.json index 6f9566bf197..2f2f3b1a5c4 100644 --- a/web/i18n/vi-VN/common.json +++ b/web/i18n/vi-VN/common.json @@ -516,6 +516,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", diff --git a/web/i18n/zh-Hans/common.json b/web/i18n/zh-Hans/common.json index ec6c72cc701..b7886a2c11b 100644 --- a/web/i18n/zh-Hans/common.json +++ b/web/i18n/zh-Hans/common.json @@ -544,6 +544,7 @@ "modelProvider.ttsModel.key": "文本转语音模型", "modelProvider.ttsModel.tip": "设置对话中文字转语音输出的默认使用模型。", "modelProvider.upgradeForLoadBalancing": "升级以解锁负载均衡功能", + "navigation.skipToMain": "跳转到主要内容", "noData": "暂无数据", "operation.add": "添加", "operation.added": "已添加", diff --git a/web/i18n/zh-Hant/common.json b/web/i18n/zh-Hant/common.json index 9a9aae7746b..ec0a0c86c9c 100644 --- a/web/i18n/zh-Hant/common.json +++ b/web/i18n/zh-Hant/common.json @@ -544,6 +544,7 @@ "modelProvider.ttsModel.key": "文字轉語音模型", "modelProvider.ttsModel.tip": "設定對話中文字轉語音輸出的預設使用模型。", "modelProvider.upgradeForLoadBalancing": "升級您的計劃以啟用 Load Balancing。", + "navigation.skipToMain": "跳至主要內容", "noData": "無資料", "operation.add": "新增", "operation.added": "已新增",