Skip to content

Commit 01d8210

Browse files
committed
Improved i18n support
1 parent 06ff64c commit 01d8210

6 files changed

Lines changed: 72 additions & 39 deletions

File tree

.changeset/snippet-spanish-translations-partial-configs.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,28 @@
22
'@embedpdf/snippet': minor
33
---
44

5-
Added Spanish translations and improved plugin configuration API.
5+
Added Spanish translations, improved i18n support, and enhanced plugin configuration API.
66

77
### New Features
88

99
- **Spanish Translations**: Added Spanish (`es`) locale support with complete translations for all UI elements and commands.
1010

11+
- **Annotation Sidebar Translations**: Sidebar titles are now properly translated using i18n keys. Added missing translation keys (`annotation.freeText`, `annotation.square`, `annotation.styles`, `annotation.defaults`) to all 5 languages.
12+
1113
### Improvements
1214

1315
- **Partial Plugin Configs**: All plugin configuration options in `PDFViewerConfig` now use `Partial<>` types, making it easier to override only the settings you need without specifying all required fields.
1416

17+
- **Reactive Blend Mode Labels**: Blend mode dropdown labels in the annotation sidebar now update reactively when the language changes.
18+
19+
- **Search Sidebar Layout**: Changed search options checkboxes from horizontal to vertical layout for better compatibility with longer translated labels.
20+
1521
```typescript
16-
// Before: Had to provide full config objects
17-
// After: Can override just specific settings
22+
// Override just specific settings
1823
<PDFViewer
1924
config={{
2025
src: '/document.pdf',
21-
zoom: { defaultZoomLevel: ZoomMode.FitWidth }, // Only override what you need
26+
zoom: { defaultZoomLevel: ZoomMode.FitWidth },
2227
i18n: { defaultLocale: 'es' }, // Use Spanish translations
2328
}}
2429
/>

viewers/snippet/src/components/annotation-sidebar.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
AnnotationTool,
66
useAnnotation,
77
} from '@embedpdf/plugin-annotation/preact';
8+
import { useTranslations } from '@embedpdf/plugin-i18n/preact';
89
import { PdfAnnotationSubtype } from '@embedpdf/models';
910
import { getAnnotationByUid } from '@embedpdf/plugin-annotation';
1011

@@ -15,6 +16,8 @@ import { EmptyState } from './annotation-sidebar/empty-state';
1516
export function AnnotationSidebar({ documentId }: { documentId: string }) {
1617
const { provides: annotationCapability } = useAnnotationCapability();
1718
const { provides: annotation, state } = useAnnotation(documentId);
19+
const { translate } = useTranslations(documentId);
20+
1821
if (!annotationCapability || !annotation) return null;
1922

2023
const colorPresets = annotationCapability?.getColorPresets() ?? [];
@@ -41,7 +44,7 @@ export function AnnotationSidebar({ documentId }: { documentId: string }) {
4144
const entry = SidebarRegistry[subtype];
4245
if (!entry) return <EmptyState documentId={documentId} />;
4346

44-
const { component: Sidebar, title } = entry;
47+
const { component: Sidebar, titleKey } = entry;
4548

4649
// 3. Prepare the simplified props for the sidebar component
4750
const commonProps: SidebarPropsBase<any> = {
@@ -51,15 +54,17 @@ export function AnnotationSidebar({ documentId }: { documentId: string }) {
5154
colorPresets,
5255
};
5356

54-
const computedTitle = typeof title === 'function' ? title(commonProps as any) : title;
57+
// 4. Get the translated title
58+
const resolvedTitleKey = typeof titleKey === 'function' ? titleKey(commonProps as any) : titleKey;
59+
const annotationType = resolvedTitleKey ? translate(resolvedTitleKey) : '';
60+
const titleSuffixKey = selectedAnnotation ? 'annotation.styles' : 'annotation.defaults';
61+
const computedTitle = annotationType
62+
? translate(titleSuffixKey, { params: { type: annotationType } })
63+
: '';
5564

5665
return (
5766
<div class="h-full overflow-y-auto p-4">
58-
{computedTitle && (
59-
<h2 class="text-md mb-4 font-medium">
60-
{computedTitle} {selectedAnnotation ? 'styles' : 'defaults'}
61-
</h2>
62-
)}
67+
{computedTitle && <h2 class="text-md mb-4 font-medium">{computedTitle}</h2>}
6368
<Sidebar {...(commonProps as any)} />
6469
</div>
6570
);

viewers/snippet/src/components/annotation-sidebar/registry.tsx

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,42 @@ type SidebarComponent<S extends PdfAnnotationSubtype> = (
1616

1717
interface SidebarEntry<S extends PdfAnnotationSubtype> {
1818
component: SidebarComponent<S>;
19-
title?: string | ((p: SidebarPropsBase<Extract<PdfAnnotationObject, { type: S }>>) => string);
19+
/** Translation key for the sidebar title (e.g., 'annotation.ink') */
20+
titleKey?: string | ((p: SidebarPropsBase<Extract<PdfAnnotationObject, { type: S }>>) => string);
2021
}
2122

2223
type SidebarRegistry = Partial<{
2324
[K in PdfAnnotationSubtype]: SidebarEntry<K>;
2425
}>;
2526

2627
export const SidebarRegistry: SidebarRegistry = {
27-
[PdfAnnotationSubtype.INK]: { component: InkSidebar, title: 'Ink' },
28-
[PdfAnnotationSubtype.POLYGON]: { component: PolygonSidebar, title: 'Polygon' },
29-
[PdfAnnotationSubtype.SQUARE]: { component: ShapeSidebar, title: 'Square' },
30-
[PdfAnnotationSubtype.CIRCLE]: { component: ShapeSidebar, title: 'Circle' },
28+
[PdfAnnotationSubtype.INK]: { component: InkSidebar, titleKey: 'annotation.ink' },
29+
[PdfAnnotationSubtype.POLYGON]: { component: PolygonSidebar, titleKey: 'annotation.polygon' },
30+
[PdfAnnotationSubtype.SQUARE]: { component: ShapeSidebar, titleKey: 'annotation.square' },
31+
[PdfAnnotationSubtype.CIRCLE]: { component: ShapeSidebar, titleKey: 'annotation.circle' },
3132

3233
[PdfAnnotationSubtype.LINE]: {
3334
component: LineSidebar,
34-
title: (p) => (p.activeTool?.id === 'lineArrow' ? 'Arrow' : 'Line'),
35+
titleKey: (p) => (p.activeTool?.id === 'lineArrow' ? 'annotation.arrow' : 'annotation.line'),
3536
},
36-
[PdfAnnotationSubtype.POLYLINE]: { component: LineSidebar, title: 'Polyline' },
37+
[PdfAnnotationSubtype.POLYLINE]: { component: LineSidebar, titleKey: 'annotation.polyline' },
3738

38-
[PdfAnnotationSubtype.HIGHLIGHT]: { component: TextMarkupSidebar, title: 'Highlight' },
39-
[PdfAnnotationSubtype.UNDERLINE]: { component: TextMarkupSidebar, title: 'Underline' },
40-
[PdfAnnotationSubtype.STRIKEOUT]: { component: TextMarkupSidebar, title: 'Strikeout' },
41-
[PdfAnnotationSubtype.SQUIGGLY]: { component: TextMarkupSidebar, title: 'Squiggly' },
42-
[PdfAnnotationSubtype.FREETEXT]: { component: FreeTextSidebar, title: 'Free text' },
43-
[PdfAnnotationSubtype.STAMP]: { component: StampSidebar, title: 'Stamp' },
39+
[PdfAnnotationSubtype.HIGHLIGHT]: {
40+
component: TextMarkupSidebar,
41+
titleKey: 'annotation.highlight',
42+
},
43+
[PdfAnnotationSubtype.UNDERLINE]: {
44+
component: TextMarkupSidebar,
45+
titleKey: 'annotation.underline',
46+
},
47+
[PdfAnnotationSubtype.STRIKEOUT]: {
48+
component: TextMarkupSidebar,
49+
titleKey: 'annotation.strikeout',
50+
},
51+
[PdfAnnotationSubtype.SQUIGGLY]: {
52+
component: TextMarkupSidebar,
53+
titleKey: 'annotation.squiggly',
54+
},
55+
[PdfAnnotationSubtype.FREETEXT]: { component: FreeTextSidebar, titleKey: 'annotation.freeText' },
56+
[PdfAnnotationSubtype.STAMP]: { component: StampSidebar, titleKey: 'annotation.stamp' },
4457
};

viewers/snippet/src/components/annotation-sidebar/text-markup-sidebar.tsx

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Fragment, h } from 'preact';
2-
import { useState, useEffect, useMemo } from 'preact/hooks';
2+
import { useState, useEffect } from 'preact/hooks';
33
import { useAnnotationCapability } from '@embedpdf/plugin-annotation/preact';
44
import { useTranslations } from '@embedpdf/plugin-i18n/preact';
55
import {
@@ -67,16 +67,6 @@ export const TextMarkupSidebar = ({
6767
const debOpacity = useDebounce(opacity, 300);
6868
useEffect(() => applyPatch({ opacity: debOpacity }), [debOpacity]);
6969

70-
// Build translated blend mode options
71-
const blendOptions = useMemo(
72-
() =>
73-
blendModeValues.map((mode) => ({
74-
value: mode,
75-
label: translate(BLEND_MODE_KEYS[mode]),
76-
})),
77-
[translate],
78-
);
79-
8070
const changeColor = (c: string) => {
8171
setColor(c);
8272
applyPatch({ color: c });
@@ -124,9 +114,9 @@ export const TextMarkupSidebar = ({
124114
value={blend}
125115
onChange={(e) => changeBlend(parseInt((e.target as HTMLSelectElement).value, 10))}
126116
>
127-
{blendOptions.map((o) => (
128-
<option key={o.value} value={o.value}>
129-
{o.label}
117+
{blendModeValues.map((mode) => (
118+
<option key={mode} value={mode}>
119+
{translate(BLEND_MODE_KEYS[mode])}
130120
</option>
131121
))}
132122
</select>

viewers/snippet/src/components/search-sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export function SearchSidebar({ documentId, onClose }: SearchSidebarProps) {
167167
</div>
168168
)}
169169
</div>
170-
<div className="mt-3 flex flex-row gap-4">
170+
<div className="mt-3 flex flex-col gap-2">
171171
<Checkbox
172172
label={translate('search.caseSensitive')}
173173
checked={state.flags.includes(MatchFlag.MatchCase)}

viewers/snippet/src/config/translations.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ export const englishTranslations: Locale = {
123123
polyline: 'Polyline',
124124
ink: 'Ink',
125125
stamp: 'Stamp',
126+
freeText: 'Free Text',
127+
square: 'Square',
128+
styles: '{type} Styles',
129+
defaults: '{type} Defaults',
126130
// Sidebar labels
127131
color: 'Color',
128132
opacity: 'Opacity',
@@ -312,6 +316,10 @@ export const germanTranslations: Locale = {
312316
polyline: 'Polylinie',
313317
ink: 'Freihand',
314318
stamp: 'Stempel',
319+
freeText: 'Freitext',
320+
square: 'Quadrat',
321+
styles: '{type}-Stile',
322+
defaults: '{type}-Standardwerte',
315323
// Sidebar labels
316324
color: 'Farbe',
317325
opacity: 'Deckkraft',
@@ -502,6 +510,10 @@ export const dutchTranslations: Locale = {
502510
polyline: 'Polylijn',
503511
ink: 'Inkt',
504512
stamp: 'Stempel',
513+
freeText: 'Vrije tekst',
514+
square: 'Vierkant',
515+
styles: '{type} stijlen',
516+
defaults: '{type} standaardwaarden',
505517
// Sidebar labels
506518
color: 'Kleur',
507519
opacity: 'Dekking',
@@ -691,6 +703,10 @@ export const frenchTranslations: Locale = {
691703
polyline: 'Polyligne',
692704
ink: 'Encre',
693705
stamp: 'Tampon',
706+
freeText: 'Texte libre',
707+
square: 'Carré',
708+
styles: 'Styles de {type}',
709+
defaults: 'Valeurs par défaut de {type}',
694710
// Sidebar labels
695711
color: 'Couleur',
696712
opacity: 'Opacité',
@@ -881,6 +897,10 @@ export const spanishTranslations: Locale = {
881897
polyline: 'Polilínea',
882898
ink: 'Tinta',
883899
stamp: 'Sello',
900+
freeText: 'Texto libre',
901+
square: 'Cuadrado',
902+
styles: 'Estilos de {type}',
903+
defaults: 'Valores predeterminados de {type}',
884904
// Sidebar labels
885905
color: 'Color',
886906
opacity: 'Opacidad',

0 commit comments

Comments
 (0)