Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/plugin-i18n/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
- **useTranslation Hook**:
- Now supports optional `documentId` parameter for document-scoped translations
- `useTranslations(documentId?)` hook for getting document-scoped translation function
- `useStaticTranslation(config?)` hook/composable for static translation resolution before plugin initialization

### New Features
- Locale registration and management
Expand Down Expand Up @@ -115,6 +116,7 @@
- **useTranslation Hook**:
- Now supports optional `documentId` parameter for document-scoped translations
- `useTranslations(documentId?)` hook for getting document-scoped translation function
- `useStaticTranslation(config?)` hook/composable for static translation resolution before plugin initialization

### New Features
- Locale registration and management
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-i18n/src/shared/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './use-i18n';
export * from './use-static-translation';
49 changes: 49 additions & 0 deletions packages/plugin-i18n/src/shared/hooks/use-static-translation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useCallback } from '@framework';
import type { Locale, TranslationDictionary } from '../../lib/types';

/**
* Resolves a translation key from locale data statically before the i18n plugin is initialized.
* Used for early loading states that appear before plugins are ready.
*/
export function getStaticTranslation(
locales: Locale[],
defaultLocale: string,
key: string,
fallback?: string,
): string {
if (!locales || locales.length === 0) return fallback ?? key;

const locale = locales.find((l) => l.code === defaultLocale) ?? locales[0];
if (!locale) return fallback ?? key;

const parts = key.split('.');
let current: TranslationDictionary | string = locale.translations;
for (const part of parts) {
if (typeof current === 'string') return fallback ?? key;
current = current[part];
if (current === undefined) return fallback ?? key;
}
return typeof current === 'string' ? current : (fallback ?? key);
}

/**
* Hook that returns a static translation function for use before dthe i18n system is ready.
*
* @param config Locales and default locale to use for resolution
* @returns A translation function that takes a key and returns the translated string
*/

const EMPTY_ARRAY: Locale[] = [];

export function useStaticTranslation(
config?: { locales?: Locale[]; defaultLocale?: string }
) {
const locales = config?.locales || EMPTY_ARRAY;
const defaultLocale = config?.defaultLocale || 'en';

return useCallback(
(key: string, fallback?: string) => getStaticTranslation(locales, defaultLocale, key, fallback),
[locales, defaultLocale]
);
}

14 changes: 11 additions & 3 deletions viewers/snippet/src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ import {
useActiveDocument,
} from '@embedpdf/plugin-document-manager/preact';
import { CommandsPluginPackage, CommandsPluginConfig } from '@embedpdf/plugin-commands/preact';
import { I18nPluginPackage, I18nPluginConfig, useTranslations } from '@embedpdf/plugin-i18n/preact';
import {
I18nPluginPackage,
I18nPluginConfig,
useTranslations,
useStaticTranslation,
} from '@embedpdf/plugin-i18n/preact';
import {
MarqueeZoom,
ZoomMode,
Expand Down Expand Up @@ -544,12 +549,15 @@ export function PDFViewer({ config, onRegistryReady }: PDFViewerProps) {
[],
);

const i18nConfig = useMemo(() => ({ ...DEFAULTS.i18n, ...config.i18n }), [config.i18n]);
const staticTranslate = useStaticTranslation(i18nConfig);

if (!engine || isLoading)
return (
<>
<style>{styles}</style>
<div className="flex h-full w-full items-center justify-center">
<LoadingIndicator size="lg" text="Initializing PDF engine..." />
<LoadingIndicator size="lg" text={staticTranslate('viewer.initializingEngine')} />
</div>
</>
);
Expand Down Expand Up @@ -689,7 +697,7 @@ export function PDFViewer({ config, onRegistryReady }: PDFViewerProps) {
</>
) : (
<div className="flex h-full items-center justify-center">
<LoadingIndicator size="lg" text="Initializing plugins..." />
<LoadingIndicator size="lg" text={staticTranslate('viewer.initializingPlugins')} />
</div>
)}
</>
Expand Down
32 changes: 32 additions & 0 deletions viewers/snippet/src/config/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export const englishTranslations: Locale = {
code: 'en',
name: 'English',
translations: {
viewer: {
initializingPlugins: 'Initializing plugins...',
initializingEngine: 'Initializing PDF engine...',
},
search: {
placeholder: 'Search',
caseSensitive: 'Case sensitive',
Expand Down Expand Up @@ -400,6 +404,10 @@ export const germanTranslations: Locale = {
code: 'de',
name: 'Deutsch',
translations: {
viewer: {
initializingPlugins: 'Plugins werden initialisiert...',
initializingEngine: 'PDF-Engine wird initialisiert...',
},
search: {
placeholder: 'Suchen',
caseSensitive: 'Groß-/Kleinschreibung',
Expand Down Expand Up @@ -795,6 +803,10 @@ export const dutchTranslations: Locale = {
code: 'nl',
name: 'Nederlands',
translations: {
viewer: {
initializingPlugins: 'Plugins initialiseren...',
initializingEngine: 'PDF-engine initialiseren...',
},
search: {
placeholder: 'Zoeken',
caseSensitive: 'Hoofdlettergevoelig',
Expand Down Expand Up @@ -1190,6 +1202,10 @@ export const frenchTranslations: Locale = {
code: 'fr',
name: 'Français',
translations: {
viewer: {
initializingPlugins: 'Initialisation des plugins...',
initializingEngine: 'Initialisation du moteur PDF...',
},
search: {
placeholder: 'Rechercher',
caseSensitive: 'Respecter la casse',
Expand Down Expand Up @@ -1585,6 +1601,10 @@ export const spanishTranslations: Locale = {
code: 'es',
name: 'Español',
translations: {
viewer: {
initializingPlugins: 'Inicializando plugins...',
initializingEngine: 'Inicializando motor PDF...',
},
search: {
placeholder: 'Buscar',
caseSensitive: 'Distinguir mayúsculas',
Expand Down Expand Up @@ -1980,6 +2000,10 @@ export const simplifiedChineseTranslations: Locale = {
code: 'zh-CN',
name: '简体中文',
translations: {
viewer: {
initializingPlugins: '正在初始化插件...',
initializingEngine: '正在初始化PDF引擎...',
},
search: {
placeholder: '搜索',
caseSensitive: '大小写敏感',
Expand Down Expand Up @@ -2369,6 +2393,10 @@ export const swedishTranslations: Locale = {
code: 'sv',
name: 'Svenska',
translations: {
viewer: {
initializingPlugins: 'Initialiserar plugins...',
initializingEngine: 'Initialiserar PDF-motor...',
},
search: {
placeholder: 'Sök',
caseSensitive: 'Skiftlägeskänslig',
Expand Down Expand Up @@ -2760,6 +2788,10 @@ export const japaneseTranslations: Locale = {
code: 'ja',
name: '日本語',
translations: {
viewer: {
initializingPlugins: 'プラグインを初期化中...',
initializingEngine: 'PDFエンジンを初期化中...',
},
search: {
placeholder: '検索',
caseSensitive: '大文字小文字を区別',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
ParamResolvers,
useTranslations,
useI18nCapability,
useStaticTranslation,
} from '@embedpdf/plugin-i18n/react'
import { GlobalStoreState } from '@embedpdf/core'
import {
Expand Down Expand Up @@ -56,6 +57,10 @@ const englishLocale: Locale = {
toolbar: {
language: 'Language',
},
viewer: {
initializingPlugins: 'Initializing plugins...',
initializingEngine: 'Initializing PDF engine...',
},
},
}

Expand All @@ -76,6 +81,10 @@ const spanishLocale: Locale = {
toolbar: {
language: 'Idioma',
},
viewer: {
initializingPlugins: 'Inicializando plugins...',
initializingEngine: 'Inicializando motor de PDF...',
},
},
}

Expand All @@ -96,6 +105,10 @@ const germanLocale: Locale = {
toolbar: {
language: 'Sprache',
},
viewer: {
initializingPlugins: 'Plugins werden initialisiert...',
initializingEngine: 'PDF-Engine wird initialisiert...',
},
},
}

Expand Down Expand Up @@ -232,13 +245,22 @@ export const PDFViewer = () => {
[],
)

const { provides } = useI18nCapability()
const currentLocale = provides?.getLocale() ?? 'en'
const staticTranslate = useStaticTranslation({
locales: [englishLocale, spanishLocale, germanLocale],
defaultLocale: currentLocale,
})

if (isLoading || !engine) {
return (
<div className="overflow-hidden rounded-lg border border-gray-300 bg-white dark:border-gray-700 dark:bg-gray-900">
<div className="flex h-[400px] items-center justify-center">
<div className="flex items-center gap-2 text-gray-500">
<Loader2 size={20} className="animate-spin" />
<span className="text-sm">Loading PDF Engine...</span>
<span className="text-sm">
{staticTranslate('viewer.initializingEngine')}
</span>
</div>
</div>
</div>
Expand Down Expand Up @@ -307,7 +329,9 @@ export const PDFViewer = () => {
<div className="flex h-[400px] items-center justify-center">
<div className="flex items-center gap-2 text-gray-500">
<Loader2 size={20} className="animate-spin" />
<span className="text-sm">Initializing plugins...</span>
<span className="text-sm">
{staticTranslate('viewer.initializingPlugins')}
</span>
</div>
</div>
</div>
Expand Down
19 changes: 19 additions & 0 deletions website/src/content/docs/react/headless/plugins/plugin-i18n.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,25 @@ A convenience hook for translating a single key. Useful for derived state or sim
const zoomLabel = useTranslation('zoom.in', undefined, documentId)
```

### Hook: `useStaticTranslation(config?)`

Returns a static translation function for use before the i18n system is fully ready. Useful for early loading states.

#### Parameters

| Parameter | Type | Description |
| :--- | :--- | :--- |
| **`config`** | `{ locales?: Locale[]; defaultLocale?: string }` | **(Optional)** Configuration for static translation resolution. |

#### Returns

A translation function `(key: string, fallback?: string) => string`.

```tsx
const translate = useStaticTranslation({ locales, defaultLocale: 'en' })
const loadingText = translate('loading.label', 'Loading...')
```

### Hook: `useLocale()`

Returns the current locale code. Updates automatically when locale changes.
Expand Down
23 changes: 23 additions & 0 deletions website/src/content/docs/svelte/headless/plugins/plugin-i18n.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,29 @@ The primary store for translating text in components. Automatically updates when
| **`params`** | `Record<string, string \| number>` | Parameters to interpolate into the translation (e.g., `{ level: 150 }`). |
| **`fallback`** | `string` | Fallback text if translation is not found. |

### Function: `useStaticTranslation(config?)`

Returns a static translation function for use before the i18n system is fully ready. Useful for early loading states.

#### Parameters

| Parameter | Type | Description |
| :--- | :--- | :--- |
| **`config`** | `{ locales?: Locale[]; defaultLocale?: string }` | **(Optional)** Configuration for static translation resolution. |

#### Returns

A translation function `(key: string, fallback?: string) => string`.

```svelte
<script lang="ts">
import { useStaticTranslation } from '@embedpdf/plugin-i18n/svelte'

const translate = useStaticTranslation({ locales, defaultLocale: 'en' })
const loadingText = translate('loading.label', 'Loading...')
</script>
```

### Store: `useI18nCapability()`

Provides access to all i18n operations.
Expand Down
21 changes: 21 additions & 0 deletions website/src/content/docs/vue/headless/plugins/plugin-i18n.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,27 @@ const zoomLabel = useTranslation('zoom.in', undefined, () => props.documentId)
</script>
```

### Composable: `useStaticTranslation(config?)`

Returns a static translation function for use before the i18n system is fully ready. Useful for early loading states.

#### Parameters

| Parameter | Type | Description |
| :--- | :--- | :--- |
| **`config`** | `{ locales?: Locale[]; defaultLocale?: string }` | **(Optional)** Configuration for static translation resolution. |

#### Returns

A translation function `(key: string, fallback?: string) => string`.

```vue
<script setup lang="ts">
const translate = useStaticTranslation({ locales, defaultLocale: 'en' })
const loadingText = translate('loading.label', 'Loading...')
</script>
```

### Composable: `useLocale()`

Returns the current locale code. Updates automatically when locale changes.
Expand Down