mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-01 00:13:37 +00:00
feat: show the accessibility and help links on the login page, in the settings sidebar and in the account menu
This commit is contained in:
@@ -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' });
|
||||
|
||||
@@ -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() {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<HelpLinks listClassName="justify-center" />
|
||||
{loginShowVersion && <VersionBadge />}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1845,6 +1847,7 @@ function LoginPageContent() {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<HelpLinks listClassName="justify-center" />
|
||||
{loginShowVersion && <VersionBadge />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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<typeof import('@/hooks/use-config')>();
|
||||
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(<HelpLinks />);
|
||||
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(<HelpLinks />);
|
||||
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(<AccountSwitcher variant="rail" />);
|
||||
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(<AccountSwitcher variant="header" />);
|
||||
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(<AccountSwitcher variant="rail" />);
|
||||
fireEvent.click(screen.getByTestId('account-switcher'));
|
||||
expect(screen.queryByRole('group', { name: 'label' })).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -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({
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<HelpMenuItems
|
||||
onSelect={() => 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({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Accessibility and help links the operator set (lib/help-links.ts). */}
|
||||
{!mvPopover && (
|
||||
<HelpMenuItems
|
||||
onSelect={() => 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. */}
|
||||
<div className={cn("border-t border-border", mvPopover && "hidden")}>
|
||||
|
||||
@@ -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 && <span className="sr-only"> ({t('new_tab')})</span>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<nav aria-label={t('label')} className={className} data-testid="help-links">
|
||||
<ul className={cn('flex flex-wrap items-center gap-x-3 gap-y-1', listClassName)}>
|
||||
{links.map((link) => (
|
||||
<li key={link.id}>
|
||||
<a
|
||||
href={link.href}
|
||||
target={link.external ? '_blank' : undefined}
|
||||
rel={link.external ? 'noopener noreferrer' : undefined}
|
||||
className={cn('text-xs text-muted-foreground underline-offset-2 hover:text-foreground hover:underline', linkClassName)}
|
||||
>
|
||||
<LinkText link={link} />
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<div role="group" aria-label={t('label')} className={className} data-testid="help-menu-items">
|
||||
{links.map((link) => (
|
||||
<a
|
||||
key={link.id}
|
||||
role="menuitem"
|
||||
href={link.href}
|
||||
target={link.external ? '_blank' : undefined}
|
||||
rel={link.external ? 'noopener noreferrer' : undefined}
|
||||
onClick={() => onSelect?.()}
|
||||
className={itemClassName}
|
||||
>
|
||||
<LinkText link={link} />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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 =
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border px-2 py-2">
|
||||
<HelpLinks className="px-3 py-2" />
|
||||
{showAdvanced && <AboutFooter />}
|
||||
<AdvancedSettingsSwitch />
|
||||
</div>
|
||||
@@ -1188,6 +1190,7 @@ export function SettingsApp({ linkSegments: routeSegments }: SettingsAppProps =
|
||||
</div>
|
||||
</nav>
|
||||
<div className="border-t border-border px-1 py-1">
|
||||
<HelpLinks className="px-3 py-2" />
|
||||
{showAdvanced && <AboutFooter />}
|
||||
<AdvancedSettingsSwitch />
|
||||
</div>
|
||||
|
||||
@@ -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<HelpLinkConfig>): 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;
|
||||
}
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS قاعدة في المتصفح. يجب أن يسمح الخادم لموقع الويب أولًا، وبعدها يسمح المتصفح لهذا الموقع بالتواصل مع الخادم."
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "إمكانية الوصول والمساعدة",
|
||||
"statement": "بيان إمكانية الوصول",
|
||||
"feedback": "الإبلاغ عن عائق",
|
||||
"help": "المساعدة",
|
||||
"easy_read": "قراءة سهلة",
|
||||
"sign_language": "لغة الإشارة",
|
||||
"new_tab": "يُفتح في علامة تبويب جديدة"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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ě"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS یک قانون مرورگر است. سرور باید اول به یک وبسایت اجازه بدهد تا مرورگر بگذارد آن وبسایت با سرور ارتباط بگیرد."
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "دسترسپذیری و راهنما",
|
||||
"statement": "بیانیه دسترسپذیری",
|
||||
"feedback": "گزارش یک مانع",
|
||||
"help": "راهنما",
|
||||
"easy_read": "متن آسانخوان",
|
||||
"sign_language": "زبان اشاره",
|
||||
"new_tab": "در زبانهای تازه باز میشود"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5912,5 +5912,14 @@
|
||||
"text": "CORS הוא כלל של הדפדפן. שרת צריך לאשר אתר קודם, ורק אז הדפדפן מאפשר לאתר הזה לדבר עם השרת."
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "נגישות ועזרה",
|
||||
"statement": "הצהרת נגישות",
|
||||
"feedback": "דיווח על מכשול",
|
||||
"help": "עזרה",
|
||||
"easy_read": "שפה פשוטה",
|
||||
"sign_language": "שפת סימנים",
|
||||
"new_tab": "נפתח בכרטיסייה חדשה"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS はブラウザーのルールです。サーバーがウェブサイトを許可しないと、ブラウザーはそのサイトがサーバーとやり取りすることを認めません。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "アクセシビリティとヘルプ",
|
||||
"statement": "アクセシビリティ方針",
|
||||
"feedback": "バリアを報告",
|
||||
"help": "ヘルプ",
|
||||
"easy_read": "わかりやすい版",
|
||||
"sign_language": "手話",
|
||||
"new_tab": "新しいタブで開きます"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS는 브라우저 규칙이에요. 서버가 먼저 웹사이트를 허용해야 브라우저가 그 웹사이트와 서버의 통신을 허락해요."
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "접근성 및 도움말",
|
||||
"statement": "접근성 선언문",
|
||||
"feedback": "장벽 신고",
|
||||
"help": "도움말",
|
||||
"easy_read": "쉬운 글",
|
||||
"sign_language": "수어",
|
||||
"new_tab": "새 탭에서 열림"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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ē"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS бол хөтчийн дүрэм юм. Сервер эхлээд вэбсайтыг зөвшөөрөх ёстой, тэгж байж хөтөч тэр вэбсайтыг сервертэй харилцахыг зөвшөөрнө."
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "Хүртээмж ба тусламж",
|
||||
"statement": "Хүртээмжийн мэдэгдэл",
|
||||
"feedback": "Саад бэрхшээл мэдээлэх",
|
||||
"help": "Тусламж",
|
||||
"easy_read": "Хялбар унших",
|
||||
"sign_language": "Дохионы хэл",
|
||||
"new_tab": "шинэ табад нээгдэнэ"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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ă"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS — это правило браузера. Сервер должен сначала разрешить сайт, и только тогда браузер позволит этому сайту общаться с сервером."
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "Специальные возможности и помощь",
|
||||
"statement": "Заявление о доступности",
|
||||
"feedback": "Сообщить о барьере",
|
||||
"help": "Помощь",
|
||||
"easy_read": "Простой язык",
|
||||
"sign_language": "Жестовый язык",
|
||||
"new_tab": "открывается в новой вкладке"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5919,5 +5919,14 @@
|
||||
"text": "CORS คือกฎของเบราว์เซอร์ เซิร์ฟเวอร์ต้องอนุญาตเว็บไซต์ก่อน เบราว์เซอร์จึงจะยอมให้เว็บไซต์นั้นคุยกับเซิร์ฟเวอร์"
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "การเข้าถึงและความช่วยเหลือ",
|
||||
"statement": "คำแถลงการเข้าถึง",
|
||||
"feedback": "แจ้งอุปสรรค",
|
||||
"help": "ความช่วยเหลือ",
|
||||
"easy_read": "ภาษาที่อ่านง่าย",
|
||||
"sign_language": "ภาษามือ",
|
||||
"new_tab": "เปิดในแท็บใหม่"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS — це правило браузера. Сервер має спершу дозволити сайт, і лише тоді браузер дозволить цьому сайту спілкуватися із сервером."
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "Доступність і довідка",
|
||||
"statement": "Заява про доступність",
|
||||
"feedback": "Повідомити про бар'єр",
|
||||
"help": "Довідка",
|
||||
"easy_read": "Легке читання",
|
||||
"sign_language": "Жестова мова",
|
||||
"new_tab": "відкривається в новій вкладці"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS 是瀏覽器的一條規則。伺服器必須先允許某個網站,瀏覽器才會讓這個網站與伺服器溝通。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "無障礙與說明",
|
||||
"statement": "無障礙聲明",
|
||||
"feedback": "回報障礙",
|
||||
"help": "說明",
|
||||
"easy_read": "易讀版",
|
||||
"sign_language": "手語",
|
||||
"new_tab": "在新分頁中開啟"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5911,5 +5911,14 @@
|
||||
"text": "CORS 是浏览器的一条规则。服务器必须先允许某个网站,浏览器才会让这个网站与服务器通信。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"help_links": {
|
||||
"label": "无障碍与帮助",
|
||||
"statement": "无障碍声明",
|
||||
"feedback": "报告障碍",
|
||||
"help": "帮助",
|
||||
"easy_read": "易读版",
|
||||
"sign_language": "手语",
|
||||
"new_tab": "在新标签页中打开"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user