fix(i18n): translate viewer + Transcribe panel controls for EN/ES locales #626
@@ -58,3 +58,20 @@ test.describe('Language selector', () => {
|
|||||||
await expect(deBtn).toHaveClass(/font-bold/);
|
await expect(deBtn).toHaveClass(/font-bold/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('Mobile nav — i18n', () => {
|
||||||
|
test('hamburger button aria-label translates to EN on narrow viewport', async ({ browser }) => {
|
||||||
|
const context = await browser.newContext({
|
||||||
|
viewport: { width: 375, height: 812 },
|
||||||
|
storageState: 'e2e/.auth/user.json'
|
||||||
|
});
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.goto('/');
|
||||||
|
await page.waitForSelector('[data-hydrated]');
|
||||||
|
await page.getByRole('banner').getByRole('button', { name: 'EN', exact: true }).click();
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Open menu' })).toBeVisible();
|
||||||
|
|
||||||
|
await context.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
"nav_conversations": "Briefwechsel",
|
"nav_conversations": "Briefwechsel",
|
||||||
"nav_admin": "Admin",
|
"nav_admin": "Admin",
|
||||||
"nav_logout": "Abmelden",
|
"nav_logout": "Abmelden",
|
||||||
|
"layout_menu_open": "Menü öffnen",
|
||||||
|
"layout_menu_close": "Menü schließen",
|
||||||
"theme_toggle_to_light": "Zu hellem Design wechseln",
|
"theme_toggle_to_light": "Zu hellem Design wechseln",
|
||||||
"theme_toggle_to_dark": "Zu dunklem Design wechseln",
|
"theme_toggle_to_dark": "Zu dunklem Design wechseln",
|
||||||
"btn_save": "Speichern",
|
"btn_save": "Speichern",
|
||||||
@@ -394,6 +396,10 @@
|
|||||||
"doc_panel_discussion_annotation_tab": "Annotation · Seite {page}",
|
"doc_panel_discussion_annotation_tab": "Annotation · Seite {page}",
|
||||||
"pdf_annotations_show": "Annotierungen anzeigen",
|
"pdf_annotations_show": "Annotierungen anzeigen",
|
||||||
"pdf_annotations_hide": "Annotierungen verbergen",
|
"pdf_annotations_hide": "Annotierungen verbergen",
|
||||||
|
"viewer_previous_page": "Zurück",
|
||||||
|
"viewer_next_page": "Weiter",
|
||||||
|
"viewer_zoom_out": "Verkleinern",
|
||||||
|
"viewer_zoom_in": "Vergrößern",
|
||||||
"upload_action": "Hochladen",
|
"upload_action": "Hochladen",
|
||||||
"upload_drop_hint": "Einzeln oder mehrere Dateien auf einmal hochladen",
|
"upload_drop_hint": "Einzeln oder mehrere Dateien auf einmal hochladen",
|
||||||
"upload_accepted_types": "PDF, JPEG, PNG, TIFF",
|
"upload_accepted_types": "PDF, JPEG, PNG, TIFF",
|
||||||
@@ -655,6 +661,7 @@
|
|||||||
"transcription_block_segmentation_only": "Nur Segmentierung",
|
"transcription_block_segmentation_only": "Nur Segmentierung",
|
||||||
"training_chip_kurrent": "Kurrent-Erkennung",
|
"training_chip_kurrent": "Kurrent-Erkennung",
|
||||||
"training_chip_segmentation": "Segmentierung",
|
"training_chip_segmentation": "Segmentierung",
|
||||||
|
"transcribe_mark_for_training": "Für Training vormerken",
|
||||||
"training_col_type": "Typ",
|
"training_col_type": "Typ",
|
||||||
"training_type_base": "Basis",
|
"training_type_base": "Basis",
|
||||||
"training_type_personalized": "Personalisiert",
|
"training_type_personalized": "Personalisiert",
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
"nav_conversations": "Letters",
|
"nav_conversations": "Letters",
|
||||||
"nav_admin": "Admin",
|
"nav_admin": "Admin",
|
||||||
"nav_logout": "Sign out",
|
"nav_logout": "Sign out",
|
||||||
|
"layout_menu_open": "Open menu",
|
||||||
|
"layout_menu_close": "Close menu",
|
||||||
"theme_toggle_to_light": "Switch to light mode",
|
"theme_toggle_to_light": "Switch to light mode",
|
||||||
"theme_toggle_to_dark": "Switch to dark mode",
|
"theme_toggle_to_dark": "Switch to dark mode",
|
||||||
"btn_save": "Save",
|
"btn_save": "Save",
|
||||||
@@ -394,6 +396,10 @@
|
|||||||
"doc_panel_discussion_annotation_tab": "Annotation · Page {page}",
|
"doc_panel_discussion_annotation_tab": "Annotation · Page {page}",
|
||||||
"pdf_annotations_show": "Show annotations",
|
"pdf_annotations_show": "Show annotations",
|
||||||
"pdf_annotations_hide": "Hide annotations",
|
"pdf_annotations_hide": "Hide annotations",
|
||||||
|
"viewer_previous_page": "Previous page",
|
||||||
|
"viewer_next_page": "Next page",
|
||||||
|
"viewer_zoom_out": "Zoom out",
|
||||||
|
"viewer_zoom_in": "Zoom in",
|
||||||
"upload_action": "Upload",
|
"upload_action": "Upload",
|
||||||
"upload_drop_hint": "Drop one or multiple files at once",
|
"upload_drop_hint": "Drop one or multiple files at once",
|
||||||
"upload_accepted_types": "PDF, JPEG, PNG, TIFF",
|
"upload_accepted_types": "PDF, JPEG, PNG, TIFF",
|
||||||
@@ -655,6 +661,7 @@
|
|||||||
"transcription_block_segmentation_only": "Segmentation only",
|
"transcription_block_segmentation_only": "Segmentation only",
|
||||||
"training_chip_kurrent": "Kurrent recognition",
|
"training_chip_kurrent": "Kurrent recognition",
|
||||||
"training_chip_segmentation": "Segmentation",
|
"training_chip_segmentation": "Segmentation",
|
||||||
|
"transcribe_mark_for_training": "Mark for OCR training",
|
||||||
"training_col_type": "Type",
|
"training_col_type": "Type",
|
||||||
"training_type_base": "Base",
|
"training_type_base": "Base",
|
||||||
"training_type_personalized": "Personalized",
|
"training_type_personalized": "Personalized",
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
"nav_conversations": "Cartas",
|
"nav_conversations": "Cartas",
|
||||||
"nav_admin": "Admin",
|
"nav_admin": "Admin",
|
||||||
"nav_logout": "Cerrar sesión",
|
"nav_logout": "Cerrar sesión",
|
||||||
|
"layout_menu_open": "Abrir menú",
|
||||||
|
"layout_menu_close": "Cerrar menú",
|
||||||
"theme_toggle_to_light": "Cambiar a modo claro",
|
"theme_toggle_to_light": "Cambiar a modo claro",
|
||||||
"theme_toggle_to_dark": "Cambiar a modo oscuro",
|
"theme_toggle_to_dark": "Cambiar a modo oscuro",
|
||||||
"btn_save": "Guardar",
|
"btn_save": "Guardar",
|
||||||
@@ -394,6 +396,10 @@
|
|||||||
"doc_panel_discussion_annotation_tab": "Anotación · Página {page}",
|
"doc_panel_discussion_annotation_tab": "Anotación · Página {page}",
|
||||||
"pdf_annotations_show": "Mostrar anotaciones",
|
"pdf_annotations_show": "Mostrar anotaciones",
|
||||||
"pdf_annotations_hide": "Ocultar anotaciones",
|
"pdf_annotations_hide": "Ocultar anotaciones",
|
||||||
|
"viewer_previous_page": "Página anterior",
|
||||||
|
"viewer_next_page": "Página siguiente",
|
||||||
|
"viewer_zoom_out": "Reducir",
|
||||||
|
"viewer_zoom_in": "Ampliar",
|
||||||
"upload_action": "Subir",
|
"upload_action": "Subir",
|
||||||
"upload_drop_hint": "Uno o varios archivos a la vez",
|
"upload_drop_hint": "Uno o varios archivos a la vez",
|
||||||
"upload_accepted_types": "PDF, JPEG, PNG, TIFF",
|
"upload_accepted_types": "PDF, JPEG, PNG, TIFF",
|
||||||
@@ -655,6 +661,7 @@
|
|||||||
"transcription_block_segmentation_only": "Solo segmentación",
|
"transcription_block_segmentation_only": "Solo segmentación",
|
||||||
"training_chip_kurrent": "Reconocimiento Kurrent",
|
"training_chip_kurrent": "Reconocimiento Kurrent",
|
||||||
"training_chip_segmentation": "Segmentación",
|
"training_chip_segmentation": "Segmentación",
|
||||||
|
"transcribe_mark_for_training": "Marcar para entrenamiento de OCR",
|
||||||
"training_col_type": "Tipo",
|
"training_col_type": "Tipo",
|
||||||
"training_type_base": "Base",
|
"training_type_base": "Base",
|
||||||
"training_type_personalized": "Personalizado",
|
"training_type_personalized": "Personalizado",
|
||||||
|
|||||||
@@ -303,7 +303,9 @@ async function handleLabelToggle(label: string) {
|
|||||||
|
|
||||||
{#if canWrite && hasBlocks}
|
{#if canWrite && hasBlocks}
|
||||||
<div class="border-t border-line px-4 py-3">
|
<div class="border-t border-line px-4 py-3">
|
||||||
<p class="mb-2 font-sans text-xs font-medium text-ink-2">Für Training vormerken</p>
|
<p class="mb-2 font-sans text-xs font-medium text-ink-2">
|
||||||
|
{m.transcribe_mark_for_training()}
|
||||||
|
</p>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
{#each [{ label: 'KURRENT_RECOGNITION', display: m.training_chip_kurrent() }, { label: 'KURRENT_SEGMENTATION', display: m.training_chip_segmentation() }] as chip (chip.label)}
|
{#each [{ label: 'KURRENT_RECOGNITION', display: m.training_chip_kurrent() }, { label: 'KURRENT_SEGMENTATION', display: m.training_chip_segmentation() }] as chip (chip.label)}
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ let {
|
|||||||
<button
|
<button
|
||||||
onclick={onPrev}
|
onclick={onPrev}
|
||||||
disabled={currentPage <= 1}
|
disabled={currentPage <= 1}
|
||||||
aria-label="Zurück"
|
aria-label={m.viewer_previous_page()}
|
||||||
class="min-h-[44px] min-w-[44px] rounded p-2 text-ink-3 transition hover:bg-surface/10 focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-1 disabled:opacity-40"
|
class="min-h-[44px] min-w-[44px] rounded p-2 text-ink-3 transition hover:bg-surface/10 focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-1 disabled:opacity-40"
|
||||||
>
|
>
|
||||||
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
@@ -51,7 +51,7 @@ let {
|
|||||||
<button
|
<button
|
||||||
onclick={onNext}
|
onclick={onNext}
|
||||||
disabled={!isLoaded || currentPage >= totalPages}
|
disabled={!isLoaded || currentPage >= totalPages}
|
||||||
aria-label="Weiter"
|
aria-label={m.viewer_next_page()}
|
||||||
class="min-h-[44px] min-w-[44px] rounded p-2 text-ink-3 transition hover:bg-surface/10 focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-1 disabled:opacity-40"
|
class="min-h-[44px] min-w-[44px] rounded p-2 text-ink-3 transition hover:bg-surface/10 focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-1 disabled:opacity-40"
|
||||||
>
|
>
|
||||||
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
@@ -64,7 +64,7 @@ let {
|
|||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<button
|
<button
|
||||||
onclick={onZoomOut}
|
onclick={onZoomOut}
|
||||||
aria-label="Verkleinern"
|
aria-label={m.viewer_zoom_out()}
|
||||||
class="min-h-[44px] min-w-[44px] rounded p-2 text-ink-3 transition hover:bg-surface/10 focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-1"
|
class="min-h-[44px] min-w-[44px] rounded p-2 text-ink-3 transition hover:bg-surface/10 focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-1"
|
||||||
>
|
>
|
||||||
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
@@ -74,7 +74,7 @@ let {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onclick={onZoomIn}
|
onclick={onZoomIn}
|
||||||
aria-label="Vergrößern"
|
aria-label={m.viewer_zoom_in()}
|
||||||
class="min-h-[44px] min-w-[44px] rounded p-2 text-ink-3 transition hover:bg-surface/10 focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-1"
|
class="min-h-[44px] min-w-[44px] rounded p-2 text-ink-3 transition hover:bg-surface/10 focus-visible:ring-2 focus-visible:ring-brand-navy focus-visible:ring-offset-1"
|
||||||
>
|
>
|
||||||
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { vi, describe, it, expect, afterEach } from 'vitest';
|
|||||||
import { cleanup, render } from 'vitest-browser-svelte';
|
import { cleanup, render } from 'vitest-browser-svelte';
|
||||||
import { page } from 'vitest/browser';
|
import { page } from 'vitest/browser';
|
||||||
|
|
||||||
|
import { m } from '$lib/paraglide/messages.js';
|
||||||
import PdfControls from './PdfControls.svelte';
|
import PdfControls from './PdfControls.svelte';
|
||||||
|
|
||||||
afterEach(cleanup);
|
afterEach(cleanup);
|
||||||
@@ -23,28 +24,28 @@ describe('PdfControls — annotation toggle visibility', () => {
|
|||||||
it('renders annotation toggle when annotationCount is greater than zero', async () => {
|
it('renders annotation toggle when annotationCount is greater than zero', async () => {
|
||||||
render(PdfControls, { ...defaultProps, annotationCount: 3 });
|
render(PdfControls, { ...defaultProps, annotationCount: 3 });
|
||||||
await expect
|
await expect
|
||||||
.element(page.getByRole('button', { name: /annotierungen anzeigen/i }))
|
.element(page.getByRole('button', { name: m.pdf_annotations_show() }))
|
||||||
.toBeInTheDocument();
|
.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not render annotation toggle when annotationCount is zero', async () => {
|
it('does not render annotation toggle when annotationCount is zero', async () => {
|
||||||
render(PdfControls, { ...defaultProps, annotationCount: 0 });
|
render(PdfControls, { ...defaultProps, annotationCount: 0 });
|
||||||
await expect
|
await expect
|
||||||
.element(page.getByRole('button', { name: /annotierungen/i }))
|
.element(page.getByRole('button', { name: m.pdf_annotations_show() }))
|
||||||
.not.toBeInTheDocument();
|
.not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PdfControls — annotation toggle label', () => {
|
describe('PdfControls — annotation toggle label', () => {
|
||||||
it('shows "Annotierungen anzeigen" label when annotations are hidden', async () => {
|
it('shows show-annotations label when annotations are hidden', async () => {
|
||||||
render(PdfControls, { ...defaultProps, annotationCount: 2, showAnnotations: false });
|
render(PdfControls, { ...defaultProps, annotationCount: 2, showAnnotations: false });
|
||||||
const btn = page.getByRole('button', { name: /annotierungen anzeigen/i });
|
const btn = page.getByRole('button', { name: m.pdf_annotations_show() });
|
||||||
await expect.element(btn).toBeInTheDocument();
|
await expect.element(btn).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows "Annotierungen verbergen" label when annotations are visible', async () => {
|
it('shows hide-annotations label when annotations are visible', async () => {
|
||||||
render(PdfControls, { ...defaultProps, annotationCount: 2, showAnnotations: true });
|
render(PdfControls, { ...defaultProps, annotationCount: 2, showAnnotations: true });
|
||||||
const btn = page.getByRole('button', { name: /annotierungen verbergen/i });
|
const btn = page.getByRole('button', { name: m.pdf_annotations_hide() });
|
||||||
await expect.element(btn).toBeInTheDocument();
|
await expect.element(btn).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -58,7 +59,9 @@ describe('PdfControls — annotation toggle contrast (WCAG 2.1 AA)', () => {
|
|||||||
});
|
});
|
||||||
const allButtons = container.querySelectorAll('button');
|
const allButtons = container.querySelectorAll('button');
|
||||||
const annotationBtn = Array.from(allButtons).find((b) =>
|
const annotationBtn = Array.from(allButtons).find((b) =>
|
||||||
b.getAttribute('aria-label')?.toLowerCase().includes('annotierungen')
|
[m.pdf_annotations_show(), m.pdf_annotations_hide()].includes(
|
||||||
|
b.getAttribute('aria-label') ?? ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
expect(annotationBtn).not.toBeNull();
|
expect(annotationBtn).not.toBeNull();
|
||||||
expect(annotationBtn!.className).toContain('text-primary');
|
expect(annotationBtn!.className).toContain('text-primary');
|
||||||
@@ -75,7 +78,9 @@ describe('PdfControls — focus rings (WCAG 2.1 §2.4.7)', () => {
|
|||||||
});
|
});
|
||||||
const allButtons = container.querySelectorAll('button');
|
const allButtons = container.querySelectorAll('button');
|
||||||
const annotationBtn = Array.from(allButtons).find((b) =>
|
const annotationBtn = Array.from(allButtons).find((b) =>
|
||||||
b.getAttribute('aria-label')?.toLowerCase().includes('annotierungen')
|
[m.pdf_annotations_show(), m.pdf_annotations_hide()].includes(
|
||||||
|
b.getAttribute('aria-label') ?? ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
expect(annotationBtn).not.toBeNull();
|
expect(annotationBtn).not.toBeNull();
|
||||||
expect(annotationBtn!.className).toContain('focus-visible:ring-2');
|
expect(annotationBtn!.className).toContain('focus-visible:ring-2');
|
||||||
@@ -86,7 +91,12 @@ describe('PdfControls — focus rings (WCAG 2.1 §2.4.7)', () => {
|
|||||||
const allButtons = container.querySelectorAll('button');
|
const allButtons = container.querySelectorAll('button');
|
||||||
const iconOnlyButtons = Array.from(allButtons).filter((b) => {
|
const iconOnlyButtons = Array.from(allButtons).filter((b) => {
|
||||||
const label = b.getAttribute('aria-label') ?? '';
|
const label = b.getAttribute('aria-label') ?? '';
|
||||||
return ['zurück', 'weiter', 'verkleinern', 'vergrößern'].includes(label.toLowerCase());
|
return [
|
||||||
|
m.viewer_previous_page(),
|
||||||
|
m.viewer_next_page(),
|
||||||
|
m.viewer_zoom_out(),
|
||||||
|
m.viewer_zoom_in()
|
||||||
|
].includes(label);
|
||||||
});
|
});
|
||||||
expect(iconOnlyButtons).toHaveLength(4);
|
expect(iconOnlyButtons).toHaveLength(4);
|
||||||
for (const btn of iconOnlyButtons) {
|
for (const btn of iconOnlyButtons) {
|
||||||
@@ -104,7 +114,9 @@ describe('PdfControls — touch targets (WCAG 2.2 §2.5.8)', () => {
|
|||||||
});
|
});
|
||||||
const allButtons = container.querySelectorAll('button');
|
const allButtons = container.querySelectorAll('button');
|
||||||
const annotationBtn = Array.from(allButtons).find((b) =>
|
const annotationBtn = Array.from(allButtons).find((b) =>
|
||||||
b.getAttribute('aria-label')?.toLowerCase().includes('annotierungen')
|
[m.pdf_annotations_show(), m.pdf_annotations_hide()].includes(
|
||||||
|
b.getAttribute('aria-label') ?? ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
expect(annotationBtn).not.toBeNull();
|
expect(annotationBtn).not.toBeNull();
|
||||||
expect(annotationBtn!.className).toContain('min-h-[44px]');
|
expect(annotationBtn!.className).toContain('min-h-[44px]');
|
||||||
@@ -118,7 +130,9 @@ describe('PdfControls — touch targets (WCAG 2.2 §2.5.8)', () => {
|
|||||||
});
|
});
|
||||||
const allButtons = container.querySelectorAll('button');
|
const allButtons = container.querySelectorAll('button');
|
||||||
const annotationBtn = Array.from(allButtons).find((b) =>
|
const annotationBtn = Array.from(allButtons).find((b) =>
|
||||||
b.getAttribute('aria-label')?.toLowerCase().includes('annotierungen')
|
[m.pdf_annotations_show(), m.pdf_annotations_hide()].includes(
|
||||||
|
b.getAttribute('aria-label') ?? ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
expect(annotationBtn).not.toBeNull();
|
expect(annotationBtn).not.toBeNull();
|
||||||
expect(annotationBtn!.className).toContain('min-w-[44px]');
|
expect(annotationBtn!.className).toContain('min-w-[44px]');
|
||||||
@@ -131,7 +145,9 @@ describe('PdfControls — touch targets (WCAG 2.2 §2.5.8)', () => {
|
|||||||
showAnnotations: false
|
showAnnotations: false
|
||||||
});
|
});
|
||||||
const btn1 = Array.from(c1.querySelectorAll('button')).find((b) =>
|
const btn1 = Array.from(c1.querySelectorAll('button')).find((b) =>
|
||||||
b.getAttribute('aria-label')?.toLowerCase().includes('annotierungen')
|
[m.pdf_annotations_show(), m.pdf_annotations_hide()].includes(
|
||||||
|
b.getAttribute('aria-label') ?? ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
expect(btn1!.getAttribute('aria-pressed')).toBe('false');
|
expect(btn1!.getAttribute('aria-pressed')).toBe('false');
|
||||||
cleanup();
|
cleanup();
|
||||||
@@ -142,7 +158,9 @@ describe('PdfControls — touch targets (WCAG 2.2 §2.5.8)', () => {
|
|||||||
showAnnotations: true
|
showAnnotations: true
|
||||||
});
|
});
|
||||||
const btn2 = Array.from(c2.querySelectorAll('button')).find((b) =>
|
const btn2 = Array.from(c2.querySelectorAll('button')).find((b) =>
|
||||||
b.getAttribute('aria-label')?.toLowerCase().includes('annotierungen')
|
[m.pdf_annotations_show(), m.pdf_annotations_hide()].includes(
|
||||||
|
b.getAttribute('aria-label') ?? ''
|
||||||
|
)
|
||||||
);
|
);
|
||||||
expect(btn2!.getAttribute('aria-pressed')).toBe('true');
|
expect(btn2!.getAttribute('aria-pressed')).toBe('true');
|
||||||
});
|
});
|
||||||
@@ -152,7 +170,12 @@ describe('PdfControls — touch targets (WCAG 2.2 §2.5.8)', () => {
|
|||||||
const allButtons = container.querySelectorAll('button');
|
const allButtons = container.querySelectorAll('button');
|
||||||
const iconOnlyButtons = Array.from(allButtons).filter((b) => {
|
const iconOnlyButtons = Array.from(allButtons).filter((b) => {
|
||||||
const label = b.getAttribute('aria-label') ?? '';
|
const label = b.getAttribute('aria-label') ?? '';
|
||||||
return ['zurück', 'weiter', 'verkleinern', 'vergrößern'].includes(label.toLowerCase());
|
return [
|
||||||
|
m.viewer_previous_page(),
|
||||||
|
m.viewer_next_page(),
|
||||||
|
m.viewer_zoom_out(),
|
||||||
|
m.viewer_zoom_in()
|
||||||
|
].includes(label);
|
||||||
});
|
});
|
||||||
expect(iconOnlyButtons).toHaveLength(4);
|
expect(iconOnlyButtons).toHaveLength(4);
|
||||||
for (const btn of iconOnlyButtons) {
|
for (const btn of iconOnlyButtons) {
|
||||||
@@ -165,7 +188,12 @@ describe('PdfControls — touch targets (WCAG 2.2 §2.5.8)', () => {
|
|||||||
const allButtons = container.querySelectorAll('button');
|
const allButtons = container.querySelectorAll('button');
|
||||||
const iconOnlyButtons = Array.from(allButtons).filter((b) => {
|
const iconOnlyButtons = Array.from(allButtons).filter((b) => {
|
||||||
const label = b.getAttribute('aria-label') ?? '';
|
const label = b.getAttribute('aria-label') ?? '';
|
||||||
return ['zurück', 'weiter', 'verkleinern', 'vergrößern'].includes(label.toLowerCase());
|
return [
|
||||||
|
m.viewer_previous_page(),
|
||||||
|
m.viewer_next_page(),
|
||||||
|
m.viewer_zoom_out(),
|
||||||
|
m.viewer_zoom_in()
|
||||||
|
].includes(label);
|
||||||
});
|
});
|
||||||
expect(iconOnlyButtons).toHaveLength(4);
|
expect(iconOnlyButtons).toHaveLength(4);
|
||||||
for (const btn of iconOnlyButtons) {
|
for (const btn of iconOnlyButtons) {
|
||||||
|
|||||||
43
frontend/src/lib/messages.spec.ts
Normal file
43
frontend/src/lib/messages.spec.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import de from '../../messages/de.json';
|
||||||
|
import en from '../../messages/en.json';
|
||||||
|
import es from '../../messages/es.json';
|
||||||
|
|
||||||
|
describe('message key parity', () => {
|
||||||
|
it('de, en, and es have identical key sets', () => {
|
||||||
|
const deKeys = Object.keys(de).sort();
|
||||||
|
const enKeys = Object.keys(en).sort();
|
||||||
|
const esKeys = Object.keys(es).sort();
|
||||||
|
expect(enKeys).toEqual(deKeys);
|
||||||
|
expect(esKeys).toEqual(deKeys);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('viewer navigation keys are present in all locales', () => {
|
||||||
|
const requiredViewerKeys = [
|
||||||
|
'viewer_previous_page',
|
||||||
|
'viewer_next_page',
|
||||||
|
'viewer_zoom_out',
|
||||||
|
'viewer_zoom_in'
|
||||||
|
];
|
||||||
|
for (const key of requiredViewerKeys) {
|
||||||
|
expect(de, `missing key in de: ${key}`).toHaveProperty(key);
|
||||||
|
expect(en, `missing key in en: ${key}`).toHaveProperty(key);
|
||||||
|
expect(es, `missing key in es: ${key}`).toHaveProperty(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transcribe mark-for-training key is present in all locales', () => {
|
||||||
|
expect(de).toHaveProperty('transcribe_mark_for_training');
|
||||||
|
expect(en).toHaveProperty('transcribe_mark_for_training');
|
||||||
|
expect(es).toHaveProperty('transcribe_mark_for_training');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('layout menu open/close keys are present in all locales', () => {
|
||||||
|
expect(de).toHaveProperty('layout_menu_open');
|
||||||
|
expect(de).toHaveProperty('layout_menu_close');
|
||||||
|
expect(en).toHaveProperty('layout_menu_open');
|
||||||
|
expect(en).toHaveProperty('layout_menu_close');
|
||||||
|
expect(es).toHaveProperty('layout_menu_open');
|
||||||
|
expect(es).toHaveProperty('layout_menu_close');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -94,7 +94,7 @@ function handleOverlayKeydown(event: KeyboardEvent) {
|
|||||||
<!-- Hamburger toggle (mobile only) -->
|
<!-- Hamburger toggle (mobile only) -->
|
||||||
<button
|
<button
|
||||||
class="ml-auto flex h-11 w-11 items-center justify-center self-center rounded text-white/70 transition-colors hover:bg-white/10 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring lg:hidden"
|
class="ml-auto flex h-11 w-11 items-center justify-center self-center rounded text-white/70 transition-colors hover:bg-white/10 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring lg:hidden"
|
||||||
aria-label={mobileNavOpen ? 'Menü schließen' : 'Menü öffnen'}
|
aria-label={mobileNavOpen ? m.layout_menu_close() : m.layout_menu_open()}
|
||||||
aria-expanded={mobileNavOpen}
|
aria-expanded={mobileNavOpen}
|
||||||
aria-controls="mobile-nav"
|
aria-controls="mobile-nav"
|
||||||
onclick={() => (mobileNavOpen = !mobileNavOpen)}
|
onclick={() => (mobileNavOpen = !mobileNavOpen)}
|
||||||
|
|||||||
Reference in New Issue
Block a user