diff --git a/app/(main)/[locale]/login/__tests__/login-page-a11y.test.tsx b/app/(main)/[locale]/login/__tests__/login-page-a11y.test.tsx index dcf34c60c..2e0de0c8f 100644 --- a/app/(main)/[locale]/login/__tests__/login-page-a11y.test.tsx +++ b/app/(main)/[locale]/login/__tests__/login-page-a11y.test.tsx @@ -31,6 +31,8 @@ vi.mock('@/hooks/use-config', () => ({ loginShowVersion: false, loginShowTotp: true, loginShowRememberMe: true, + accessibilityStatementUrl: 'https://example.org/accessibility', + accessibilityFeedbackUrl: 'mailto:barriers@example.org', }), })); @@ -55,6 +57,13 @@ describe('login form accessibility', () => { expect(document.title).toBe('sign_in - Webmail'); }, 30_000); + it('links the operator\'s accessibility statement and barrier report in the footer (OP-01)', async () => { + await renderForm(); + const nav = screen.getByRole('navigation', { name: 'label' }); + expect(nav.querySelector('a[href="https://example.org/accessibility"]')).not.toBeNull(); + expect(nav.querySelector('a[href="mailto:barriers@example.org"]')).not.toBeNull(); + }, 30_000); + it('puts the show-password toggle in the tab order with its state', async () => { await renderForm(); const toggle = screen.getByRole('button', { name: 'show_password' }); diff --git a/app/(main)/[locale]/login/page.tsx b/app/(main)/[locale]/login/page.tsx index a06c77ae0..4bc5ae035 100644 --- a/app/(main)/[locale]/login/page.tsx +++ b/app/(main)/[locale]/login/page.tsx @@ -35,6 +35,7 @@ import { toAsciiDomain } from "@/lib/idn"; import { usePrefetchMailView } from "@/hooks/use-prefetch-mail-view"; import { buildMobileHandoffUrl, mobileHandoffTarget, signedInLoginAction } from "@/lib/auth/mobile-handoff"; import { MobileHandoffConsent } from "@/components/auth/mobile-handoff-consent"; +import { HelpLinks } from "@/components/layout/help-links"; import { consumeSessionEnd, type SessionEndReason } from "@/lib/session-policy/sign-out-reason"; /** The domain of a complete address (`user@example.com`), or '' while it is still being typed. */ @@ -1222,6 +1223,7 @@ function LoginPageContent() { )} )} + {loginShowVersion && } @@ -1845,6 +1847,7 @@ function LoginPageContent() { )} )} + {loginShowVersion && } diff --git a/components/layout/__tests__/help-links.test.tsx b/components/layout/__tests__/help-links.test.tsx new file mode 100644 index 000000000..f2786b943 --- /dev/null +++ b/components/layout/__tests__/help-links.test.tsx @@ -0,0 +1,109 @@ +import { fireEvent, render, screen, within } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +// OP-01: the accessibility and help links the operator configures show in +// the same order wherever they appear (WCAG 3.2.6), and not at all when +// none is set. + +const links = vi.hoisted(() => ({ + accessibilityStatementUrl: '', + accessibilityFeedbackUrl: '', + helpUrl: '', + easyReadUrl: '', + signLanguageUrl: '', +})); +vi.mock('@/hooks/use-config', async (importOriginal) => { + const actual = await importOriginal(); + return { ...actual, useConfig: () => ({ ...links }) }; +}); + +import { HelpLinks } from '../help-links'; +import { helpLinksFrom } from '@/lib/help-links'; +const { AccountSwitcher } = await import('../account-switcher'); +const { useAccountStore } = await import('@/stores/account-store'); +const { useAuthStore } = await import('@/stores/auth-store'); +const { useSettingsStore } = await import('@/stores/settings-store'); + +const account = { + id: 'a', label: 'a@example.com', email: 'a@example.com', username: 'a@example.com', displayName: 'A', + serverUrl: 'https://mail.example.com', avatarColor: '#336699', cookieSlot: 0, isConnected: true, isDefault: true, +}; + +beforeEach(() => { + Object.assign(links, { + accessibilityStatementUrl: 'https://example.com/accessibility', + accessibilityFeedbackUrl: 'mailto:barriers@example.com', + helpUrl: '', + easyReadUrl: 'https://example.com/leichte-sprache', + signLanguageUrl: '', + }); + useAccountStore.setState({ accounts: [account] } as never); + useAuthStore.setState({ activeAccountId: 'a' } as never); + useSettingsStore.setState({ interfaceLayout: 'default' } as never); +}); + +describe('helpLinksFrom', () => { + it('keeps the fixed order, leaves out unset links and refuses script URLs', () => { + expect(helpLinksFrom({ + signLanguageUrl: 'https://example.com/dgs', + helpUrl: 'javascript:alert(1)', + accessibilityStatementUrl: 'https://example.com/a11y', + accessibilityFeedbackUrl: 'mailto:barriers@example.com', + easyReadUrl: '', + })).toEqual([ + { id: 'statement', href: 'https://example.com/a11y', external: true }, + { id: 'feedback', href: 'mailto:barriers@example.com', external: false }, + { id: 'sign_language', href: 'https://example.com/dgs', external: true }, + ]); + }); +}); + +describe('help links on a page', () => { + it('lists the configured links in a named navigation', () => { + render(); + const nav = screen.getByRole('navigation', { name: 'label' }); + const items = within(nav).getAllByRole('link'); + expect(items.map((a) => a.getAttribute('href'))).toEqual([ + 'https://example.com/accessibility', + 'mailto:barriers@example.com', + 'https://example.com/leichte-sprache', + ]); + // A web page opens in a new tab and says so; the mail link doesn't. + expect(items[0].getAttribute('target')).toBe('_blank'); + expect(items[0].textContent).toBe('statement (new_tab)'); + expect(items[1].getAttribute('target')).toBeNull(); + expect(items[1].textContent).toBe('feedback'); + }); + + it('renders nothing when the operator set no link', () => { + Object.assign(links, { accessibilityStatementUrl: '', accessibilityFeedbackUrl: '', easyReadUrl: '' }); + const { container } = render(); + expect(container.innerHTML).toBe(''); + }); +}); + +describe('help links in the account menu', () => { + it('offers them as menu items and closes the menu on choosing one', () => { + render(); + fireEvent.click(screen.getByTestId('account-switcher')); + const group = screen.getByRole('group', { name: 'label' }); + const items = within(group).getAllByRole('menuitem'); + expect(items.map((a) => a.textContent)).toEqual(['statement (new_tab)', 'feedback', 'easy_read (new_tab)']); + fireEvent.click(items[0]); + expect(screen.queryByRole('menu')).toBeNull(); + }); + + it('offers them under Mountain View too', () => { + useSettingsStore.setState({ interfaceLayout: 'mountain-view' } as never); + render(); + fireEvent.click(screen.getByTestId('account-switcher')); + expect(within(screen.getByRole('group', { name: 'label' })).getAllByRole('menuitem')).toHaveLength(3); + }); + + it('leaves the group out when no link is set', () => { + Object.assign(links, { accessibilityStatementUrl: '', accessibilityFeedbackUrl: '', easyReadUrl: '' }); + render(); + fireEvent.click(screen.getByTestId('account-switcher')); + expect(screen.queryByRole('group', { name: 'label' })).toBeNull(); + }); +}); diff --git a/components/layout/account-switcher.tsx b/components/layout/account-switcher.tsx index 08a0b14ab..0f3218b19 100644 --- a/components/layout/account-switcher.tsx +++ b/components/layout/account-switcher.tsx @@ -9,6 +9,7 @@ import { useAccountStore, type AccountEntry } from "@/stores/account-store"; import { useAuthStore } from "@/stores/auth-store"; import { useEmailStore } from "@/stores/email-store"; import { useSettingsStore } from "@/stores/settings-store"; +import { HelpMenuItems } from "@/components/layout/help-links"; import { getMaxAccounts, sortDefaultFirst, reorderNonDefaultIds } from "@/lib/account-utils"; import { inboxUnread, totalInboxUnread } from "@/lib/account-inbox-unread"; import { isDocumentRTL } from "@/i18n/direction"; @@ -564,6 +565,11 @@ export function AccountSwitcher({ })} )} + setOpen(false)} + className="mt-2 rounded-3xl bg-background py-1" + itemClassName="w-full flex items-center px-5 py-3 text-sm text-start hover:bg-muted transition-colors" + /> )} @@ -582,6 +588,15 @@ export function AccountSwitcher({ )} + {/* Accessibility and help links the operator set (lib/help-links.ts). */} + {!mvPopover && ( + setOpen(false)} + className="border-t border-border py-1" + itemClassName="w-full flex items-center px-3 py-2 pointer-coarse:min-h-11 text-sm text-start text-foreground hover:bg-muted transition-colors" + /> + )} + {/* Separator + Actions. Under Mountain View a single login signs out from the pill above, so these rows are only for picking among several. */}
diff --git a/components/layout/help-links.tsx b/components/layout/help-links.tsx new file mode 100644 index 000000000..ca59cb262 --- /dev/null +++ b/components/layout/help-links.tsx @@ -0,0 +1,81 @@ +'use client'; + +import { useTranslations } from 'next-intl'; +import { useConfig } from '@/hooks/use-config'; +import { helpLinksFrom, type HelpLink } from '@/lib/help-links'; +import { cn } from '@/lib/utils'; + +/** The configured accessibility and help links, in their fixed order. */ +export function useHelpLinks(): HelpLink[] { + const { accessibilityStatementUrl, accessibilityFeedbackUrl, helpUrl, easyReadUrl, signLanguageUrl } = useConfig(); + return helpLinksFrom({ accessibilityStatementUrl, accessibilityFeedbackUrl, helpUrl, easyReadUrl, signLanguageUrl }); +} + +function LinkText({ link }: { link: HelpLink }) { + const t = useTranslations('help_links'); + return ( + <> + {t(link.id)} + {link.external && ({t('new_tab')})} + + ); +} + +/** + * The links as a small navigation list, for the login page footer and the + * settings sidebar. Renders nothing when the operator set none. + */ +export function HelpLinks({ className, listClassName, linkClassName }: { className?: string; listClassName?: string; linkClassName?: string }) { + const t = useTranslations('help_links'); + const links = useHelpLinks(); + if (links.length === 0) return null; + return ( + + ); +} + +/** + * The links as menu items, for the account menu. `onSelect` closes the + * menu. Renders nothing when the operator set none. + */ +export function HelpMenuItems({ onSelect, className, itemClassName }: { + onSelect?: () => void; + className?: string; + itemClassName?: string; +}) { + const t = useTranslations('help_links'); + const links = useHelpLinks(); + if (links.length === 0) return null; + return ( +
+ {links.map((link) => ( + onSelect?.()} + className={itemClassName} + > + + + ))} +
+ ); +} diff --git a/components/settings/settings-app.tsx b/components/settings/settings-app.tsx index 52f70f9e7..b5df24027 100644 --- a/components/settings/settings-app.tsx +++ b/components/settings/settings-app.tsx @@ -47,6 +47,7 @@ import { CalendarManagementSettings } from '@/components/settings/calendar-manag import { AddressBookManagementSettings } from '@/components/settings/address-book-management-settings'; import { FilterSettings } from '@/components/settings/filter-settings'; import { AboutDataSettings, AboutFooter } from '@/components/settings/about-data-settings'; +import { HelpLinks } from '@/components/layout/help-links'; import { OrganizeSettings } from '@/components/settings/organize-settings'; import { AccountSecuritySettings } from '@/components/settings/account-security-settings'; import { FilesSettingsComponent } from '@/components/settings/files-settings'; @@ -1032,6 +1033,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
+ {showAdvanced && }
@@ -1188,6 +1190,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
+ {showAdvanced && }
diff --git a/lib/help-links.ts b/lib/help-links.ts new file mode 100644 index 000000000..5791ff9e7 --- /dev/null +++ b/lib/help-links.ts @@ -0,0 +1,45 @@ +import type { ConfigData } from '@/hooks/use-config'; + +/** + * The accessibility and help links an operator configures (OP-01): the + * accessibility statement, where to report a barrier, help, Easy Read + * (Leichte Sprache) and sign language pages. They show in the same order + * in every place that lists them (WCAG 3.2.6): the login page footer, the + * settings sidebar and the account menu. Links that aren't set are left out. + */ +export type HelpLinkId = 'statement' | 'feedback' | 'help' | 'easy_read' | 'sign_language'; + +export interface HelpLink { + id: HelpLinkId; + href: string; + /** mailto: links open the mail app, not a new tab. */ + external: boolean; +} + +export type HelpLinkConfig = Pick< + ConfigData, + 'accessibilityStatementUrl' | 'accessibilityFeedbackUrl' | 'helpUrl' | 'easyReadUrl' | 'signLanguageUrl' +>; + +const ORDER: ReadonlyArray<[HelpLinkId, keyof HelpLinkConfig]> = [ + ['statement', 'accessibilityStatementUrl'], + ['feedback', 'accessibilityFeedbackUrl'], + ['help', 'helpUrl'], + ['easy_read', 'easyReadUrl'], + ['sign_language', 'signLanguageUrl'], +]; + +/** + * The configured links, in their fixed order. The values were checked when + * the config was served (http(s) or mailto: only); this checks again so a + * config from elsewhere can't put a script URL in a link. + */ +export function helpLinksFrom(config: Partial): HelpLink[] { + const out: HelpLink[] = []; + for (const [id, key] of ORDER) { + const href = typeof config[key] === 'string' ? config[key]!.trim() : ''; + if (!/^(https?:\/\/|mailto:)/i.test(href)) continue; + out.push({ id, href, external: !/^mailto:/i.test(href) }); + } + return out; +} diff --git a/locales/ar/common.json b/locales/ar/common.json index 1da191ca1..4243faa07 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -5911,5 +5911,14 @@ "text": "CORS قاعدة في المتصفح. يجب أن يسمح الخادم لموقع الويب أولًا، وبعدها يسمح المتصفح لهذا الموقع بالتواصل مع الخادم." } } + }, + "help_links": { + "label": "إمكانية الوصول والمساعدة", + "statement": "بيان إمكانية الوصول", + "feedback": "الإبلاغ عن عائق", + "help": "المساعدة", + "easy_read": "قراءة سهلة", + "sign_language": "لغة الإشارة", + "new_tab": "يُفتح في علامة تبويب جديدة" } } diff --git a/locales/ca/common.json b/locales/ca/common.json index efb866cc0..0c5a08163 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -5911,5 +5911,14 @@ "text": "CORS és una regla del navegador. Un servidor ha de permetre primer un lloc web perquè el navegador deixi que aquest lloc parli amb el servidor." } } + }, + "help_links": { + "label": "Accessibilitat i ajuda", + "statement": "Declaració d'accessibilitat", + "feedback": "Informa d'una barrera", + "help": "Ajuda", + "easy_read": "Lectura fàcil", + "sign_language": "Llengua de signes", + "new_tab": "s'obre en una pestanya nova" } } diff --git a/locales/cs/common.json b/locales/cs/common.json index 467516244..16797cc6e 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -5911,5 +5911,14 @@ "text": "CORS je pravidlo prohlížeče. Server musí webovou stránku nejdřív povolit, teprve pak ji prohlížeč nechá se serverem komunikovat." } } + }, + "help_links": { + "label": "Přístupnost a nápověda", + "statement": "Prohlášení o přístupnosti", + "feedback": "Nahlásit bariéru", + "help": "Nápověda", + "easy_read": "Snadné čtení", + "sign_language": "Znakový jazyk", + "new_tab": "otevře se na nové kartě" } } diff --git a/locales/da/common.json b/locales/da/common.json index c8edd5a6f..1e96b2a9a 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -5911,5 +5911,14 @@ "text": "CORS er en regel i browseren. En server skal først tillade en hjemmeside, før browseren lader den hjemmeside tale med serveren." } } + }, + "help_links": { + "label": "Tilgængelighed og hjælp", + "statement": "Tilgængelighedserklæring", + "feedback": "Rapportér en barriere", + "help": "Hjælp", + "easy_read": "Let læst", + "sign_language": "Tegnsprog", + "new_tab": "åbner i en ny fane" } } diff --git a/locales/de/common.json b/locales/de/common.json index a6d6dfb38..d08ffdc69 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -5911,5 +5911,14 @@ "text": "CORS ist eine Regel im Browser. Ein Server muss eine Website erst erlauben, bevor der Browser diese Website mit dem Server sprechen lässt." } } + }, + "help_links": { + "label": "Barrierefreiheit und Hilfe", + "statement": "Erklärung zur Barrierefreiheit", + "feedback": "Barriere melden", + "help": "Hilfe", + "easy_read": "Leichte Sprache", + "sign_language": "Gebärdensprache", + "new_tab": "öffnet in einem neuen Tab" } } diff --git a/locales/en/common.json b/locales/en/common.json index 35f5a08a6..69583b6d7 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -5911,5 +5911,14 @@ "text": "CORS is a browser rule. A server has to allow a website before the browser lets that website talk to the server." } } + }, + "help_links": { + "label": "Accessibility and help", + "statement": "Accessibility statement", + "feedback": "Report a barrier", + "help": "Help", + "easy_read": "Easy Read", + "sign_language": "Sign language", + "new_tab": "opens in a new tab" } } diff --git a/locales/es/common.json b/locales/es/common.json index 10e3f9e84..d68b758a7 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -5911,5 +5911,14 @@ "text": "CORS es una regla del navegador. Un servidor tiene que permitir antes un sitio web para que el navegador deje que ese sitio hable con el servidor." } } + }, + "help_links": { + "label": "Accesibilidad y ayuda", + "statement": "Declaración de accesibilidad", + "feedback": "Informar de una barrera", + "help": "Ayuda", + "easy_read": "Lectura fácil", + "sign_language": "Lengua de signos", + "new_tab": "se abre en una pestaña nueva" } } diff --git a/locales/et/common.json b/locales/et/common.json index a5a060369..d62b534de 100644 --- a/locales/et/common.json +++ b/locales/et/common.json @@ -5911,5 +5911,14 @@ "text": "CORS on brauseri reegel. Server peab veebisaidi esmalt lubama, alles siis laseb brauser sellel veebisaidil serveriga suhelda." } } + }, + "help_links": { + "label": "Juurdepääsetavus ja abi", + "statement": "Juurdepääsetavuse teatis", + "feedback": "Teata takistusest", + "help": "Abi", + "easy_read": "Lihtne keel", + "sign_language": "Viipekeel", + "new_tab": "avaneb uuel vahekaardil" } } diff --git a/locales/fa/common.json b/locales/fa/common.json index ac352dcb9..9513c8d51 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -5911,5 +5911,14 @@ "text": "CORS یک قانون مرورگر است. سرور باید اول به یک وب‌سایت اجازه بدهد تا مرورگر بگذارد آن وب‌سایت با سرور ارتباط بگیرد." } } + }, + "help_links": { + "label": "دسترس‌پذیری و راهنما", + "statement": "بیانیه دسترس‌پذیری", + "feedback": "گزارش یک مانع", + "help": "راهنما", + "easy_read": "متن آسان‌خوان", + "sign_language": "زبان اشاره", + "new_tab": "در زبانه‌ای تازه باز می‌شود" } } diff --git a/locales/fr/common.json b/locales/fr/common.json index f951c0e8e..6479c48e7 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -5911,5 +5911,14 @@ "text": "CORS est une règle du navigateur. Un serveur doit d'abord autoriser un site web avant que le navigateur laisse ce site communiquer avec le serveur." } } + }, + "help_links": { + "label": "Accessibilité et aide", + "statement": "Déclaration d'accessibilité", + "feedback": "Signaler un obstacle", + "help": "Aide", + "easy_read": "Facile à lire", + "sign_language": "Langue des signes", + "new_tab": "s'ouvre dans un nouvel onglet" } } diff --git a/locales/he/common.json b/locales/he/common.json index fd7dfdeff..5694a6c94 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -5912,5 +5912,14 @@ "text": "CORS הוא כלל של הדפדפן. שרת צריך לאשר אתר קודם, ורק אז הדפדפן מאפשר לאתר הזה לדבר עם השרת." } } + }, + "help_links": { + "label": "נגישות ועזרה", + "statement": "הצהרת נגישות", + "feedback": "דיווח על מכשול", + "help": "עזרה", + "easy_read": "שפה פשוטה", + "sign_language": "שפת סימנים", + "new_tab": "נפתח בכרטיסייה חדשה" } } diff --git a/locales/hu/common.json b/locales/hu/common.json index 4ca1dd620..383453b1f 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -5911,5 +5911,14 @@ "text": "A CORS egy böngészőszabály. A szervernek először engedélyeznie kell egy weboldalt, csak utána engedi a böngésző, hogy az a weboldal a szerverrel kommunikáljon." } } + }, + "help_links": { + "label": "Kisegítő lehetőségek és súgó", + "statement": "Akadálymentességi nyilatkozat", + "feedback": "Akadály bejelentése", + "help": "Súgó", + "easy_read": "Könnyen érthető", + "sign_language": "Jelnyelv", + "new_tab": "új lapon nyílik meg" } } diff --git a/locales/it/common.json b/locales/it/common.json index e068ee571..117760a22 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -5911,5 +5911,14 @@ "text": "CORS è una regola del browser. Un server deve autorizzare un sito web prima che il browser permetta a quel sito di comunicare con il server." } } + }, + "help_links": { + "label": "Accessibilità e aiuto", + "statement": "Dichiarazione di accessibilità", + "feedback": "Segnala una barriera", + "help": "Aiuto", + "easy_read": "Facile da leggere", + "sign_language": "Lingua dei segni", + "new_tab": "si apre in una nuova scheda" } } diff --git a/locales/ja/common.json b/locales/ja/common.json index f755b0cc9..fb47fe8fb 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -5911,5 +5911,14 @@ "text": "CORS はブラウザーのルールです。サーバーがウェブサイトを許可しないと、ブラウザーはそのサイトがサーバーとやり取りすることを認めません。" } } + }, + "help_links": { + "label": "アクセシビリティとヘルプ", + "statement": "アクセシビリティ方針", + "feedback": "バリアを報告", + "help": "ヘルプ", + "easy_read": "わかりやすい版", + "sign_language": "手話", + "new_tab": "新しいタブで開きます" } } diff --git a/locales/ko/common.json b/locales/ko/common.json index 083c6959e..75d658c51 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -5911,5 +5911,14 @@ "text": "CORS는 브라우저 규칙이에요. 서버가 먼저 웹사이트를 허용해야 브라우저가 그 웹사이트와 서버의 통신을 허락해요." } } + }, + "help_links": { + "label": "접근성 및 도움말", + "statement": "접근성 선언문", + "feedback": "장벽 신고", + "help": "도움말", + "easy_read": "쉬운 글", + "sign_language": "수어", + "new_tab": "새 탭에서 열림" } } diff --git a/locales/lv/common.json b/locales/lv/common.json index 33b6609a8..bc658849c 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -5911,5 +5911,14 @@ "text": "CORS ir pārlūka noteikums. Serverim vispirms ir jāatļauj vietne, un tikai tad pārlūks ļauj šai vietnei sazināties ar serveri." } } + }, + "help_links": { + "label": "Pieejamība un palīdzība", + "statement": "Pieejamības paziņojums", + "feedback": "Ziņot par šķērsli", + "help": "Palīdzība", + "easy_read": "Vieglā valoda", + "sign_language": "Zīmju valoda", + "new_tab": "atveras jaunā cilnē" } } diff --git a/locales/mn/common.json b/locales/mn/common.json index 5b324eb01..adefdebf3 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -5911,5 +5911,14 @@ "text": "CORS бол хөтчийн дүрэм юм. Сервер эхлээд вэбсайтыг зөвшөөрөх ёстой, тэгж байж хөтөч тэр вэбсайтыг сервертэй харилцахыг зөвшөөрнө." } } + }, + "help_links": { + "label": "Хүртээмж ба тусламж", + "statement": "Хүртээмжийн мэдэгдэл", + "feedback": "Саад бэрхшээл мэдээлэх", + "help": "Тусламж", + "easy_read": "Хялбар унших", + "sign_language": "Дохионы хэл", + "new_tab": "шинэ табад нээгдэнэ" } } diff --git a/locales/nb/common.json b/locales/nb/common.json index 30c52435c..45c41b6bd 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -5911,5 +5911,14 @@ "text": "CORS er en regel i nettleseren. En server må tillate et nettsted før nettleseren lar det nettstedet snakke med serveren." } } + }, + "help_links": { + "label": "Tilgjengelighet og hjelp", + "statement": "Tilgjengelighetserklæring", + "feedback": "Meld fra om en barriere", + "help": "Hjelp", + "easy_read": "Lettlest", + "sign_language": "Tegnspråk", + "new_tab": "åpnes i en ny fane" } } diff --git a/locales/nl/common.json b/locales/nl/common.json index adbc8ac51..54f8a9210 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -5911,5 +5911,14 @@ "text": "CORS is een regel in de browser. Een server moet een website eerst toestaan voordat de browser die website met de server laat praten." } } + }, + "help_links": { + "label": "Toegankelijkheid en help", + "statement": "Toegankelijkheidsverklaring", + "feedback": "Meld een drempel", + "help": "Help", + "easy_read": "Makkelijk lezen", + "sign_language": "Gebarentaal", + "new_tab": "opent in een nieuw tabblad" } } diff --git a/locales/pl/common.json b/locales/pl/common.json index d145c9fbf..22f8ae787 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -5911,5 +5911,14 @@ "text": "CORS to reguła przeglądarki. Serwer musi najpierw zezwolić na stronę internetową, zanim przeglądarka pozwoli tej stronie rozmawiać z serwerem." } } + }, + "help_links": { + "label": "Dostępność i pomoc", + "statement": "Deklaracja dostępności", + "feedback": "Zgłoś barierę", + "help": "Pomoc", + "easy_read": "Tekst łatwy do czytania", + "sign_language": "Język migowy", + "new_tab": "otwiera się w nowej karcie" } } diff --git a/locales/pt/common.json b/locales/pt/common.json index 7c2350d67..14f67317c 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -5911,5 +5911,14 @@ "text": "CORS é uma regra do navegador. Um servidor precisa permitir um site antes que o navegador deixe esse site conversar com o servidor." } } + }, + "help_links": { + "label": "Acessibilidade e ajuda", + "statement": "Declaração de acessibilidade", + "feedback": "Relatar uma barreira", + "help": "Ajuda", + "easy_read": "Leitura fácil", + "sign_language": "Língua de sinais", + "new_tab": "abre em uma nova aba" } } diff --git a/locales/ro/common.json b/locales/ro/common.json index e85581390..b01a7395a 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -5911,5 +5911,14 @@ "text": "CORS este o regulă a browserului. Un server trebuie mai întâi să permită un site, apoi browserul lasă acel site să comunice cu serverul." } } + }, + "help_links": { + "label": "Accesibilitate și ajutor", + "statement": "Declarație de accesibilitate", + "feedback": "Raportează o barieră", + "help": "Ajutor", + "easy_read": "Ușor de citit", + "sign_language": "Limbaj mimico-gestual", + "new_tab": "se deschide într-o filă nouă" } } diff --git a/locales/ru/common.json b/locales/ru/common.json index f8444dc1d..1b972388c 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -5911,5 +5911,14 @@ "text": "CORS — это правило браузера. Сервер должен сначала разрешить сайт, и только тогда браузер позволит этому сайту общаться с сервером." } } + }, + "help_links": { + "label": "Специальные возможности и помощь", + "statement": "Заявление о доступности", + "feedback": "Сообщить о барьере", + "help": "Помощь", + "easy_read": "Простой язык", + "sign_language": "Жестовый язык", + "new_tab": "открывается в новой вкладке" } } diff --git a/locales/sk/common.json b/locales/sk/common.json index b5579fc94..c2f2c292d 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -5911,5 +5911,14 @@ "text": "CORS je pravidlo prehliadača. Server musí webovú stránku najprv povoliť, až potom ju prehliadač nechá komunikovať so serverom." } } + }, + "help_links": { + "label": "Prístupnosť a nápoveda", + "statement": "Vyhlásenie o prístupnosti", + "feedback": "Nahlásiť bariéru", + "help": "Nápoveda", + "easy_read": "Ľahké čítanie", + "sign_language": "Posunkový jazyk", + "new_tab": "otvorí sa na novej karte" } } diff --git a/locales/th/common.json b/locales/th/common.json index 5c9bd4c65..348df9fef 100644 --- a/locales/th/common.json +++ b/locales/th/common.json @@ -5919,5 +5919,14 @@ "text": "CORS คือกฎของเบราว์เซอร์ เซิร์ฟเวอร์ต้องอนุญาตเว็บไซต์ก่อน เบราว์เซอร์จึงจะยอมให้เว็บไซต์นั้นคุยกับเซิร์ฟเวอร์" } } + }, + "help_links": { + "label": "การเข้าถึงและความช่วยเหลือ", + "statement": "คำแถลงการเข้าถึง", + "feedback": "แจ้งอุปสรรค", + "help": "ความช่วยเหลือ", + "easy_read": "ภาษาที่อ่านง่าย", + "sign_language": "ภาษามือ", + "new_tab": "เปิดในแท็บใหม่" } } diff --git a/locales/tr/common.json b/locales/tr/common.json index 295eb1493..9e4fa3e02 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -5911,5 +5911,14 @@ "text": "CORS bir tarayıcı kuralıdır. Tarayıcının bir web sitesinin sunucuyla konuşmasına izin vermesi için önce sunucunun o siteye izin vermesi gerekir." } } + }, + "help_links": { + "label": "Erişilebilirlik ve yardım", + "statement": "Erişilebilirlik beyanı", + "feedback": "Engel bildir", + "help": "Yardım", + "easy_read": "Kolay okunur", + "sign_language": "İşaret dili", + "new_tab": "yeni sekmede açılır" } } diff --git a/locales/uk/common.json b/locales/uk/common.json index ab4d49ac9..0ffebff5d 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -5911,5 +5911,14 @@ "text": "CORS — це правило браузера. Сервер має спершу дозволити сайт, і лише тоді браузер дозволить цьому сайту спілкуватися із сервером." } } + }, + "help_links": { + "label": "Доступність і довідка", + "statement": "Заява про доступність", + "feedback": "Повідомити про бар'єр", + "help": "Довідка", + "easy_read": "Легке читання", + "sign_language": "Жестова мова", + "new_tab": "відкривається в новій вкладці" } } diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 372d29a7e..1f09fe781 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -5911,5 +5911,14 @@ "text": "CORS 是瀏覽器的一條規則。伺服器必須先允許某個網站,瀏覽器才會讓這個網站與伺服器溝通。" } } + }, + "help_links": { + "label": "無障礙與說明", + "statement": "無障礙聲明", + "feedback": "回報障礙", + "help": "說明", + "easy_read": "易讀版", + "sign_language": "手語", + "new_tab": "在新分頁中開啟" } } diff --git a/locales/zh/common.json b/locales/zh/common.json index 295169811..41b20e2e4 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -5911,5 +5911,14 @@ "text": "CORS 是浏览器的一条规则。服务器必须先允许某个网站,浏览器才会让这个网站与服务器通信。" } } + }, + "help_links": { + "label": "无障碍与帮助", + "statement": "无障碍声明", + "feedback": "报告障碍", + "help": "帮助", + "easy_read": "易读版", + "sign_language": "手语", + "new_tab": "在新标签页中打开" } }