EmbedPDF

Translations

The translations plugin holds your viewer’s text in every language you support, and switches between them while the app runs. Register it with the user’s language:

i18n-plugin.svelte
<script lang="ts">
  import { i18nPlugin, negotiateLocale } from '@embedpdf/svelte/i18n';
  import { Viewer } from '@embedpdf/svelte/runtime';
  import en from './locales/en';
  import { engine } from './pdf';

  const locale = negotiateLocale(['en', 'nl', 'de'], navigator.languages) ?? 'en';

  const plugins = [
    /* … */
    i18nPlugin({
      locale,
      locales: [{ code: 'en', name: 'English', translations: en }],
      loaders: {
        nl: () => import('./locales/nl').then((module) => module.default),
        de: () => import('./locales/de').then((module) => module.default),
      },
    }),
  ];
</script>

<Viewer {engine} {plugins}>
  <!-- your toolbar and pages -->
</Viewer>
Then translate with useT():
save-button.svelte
<script lang="ts">
  import { useT } from '@embedpdf/svelte/i18n';

  const t = useT();
</script>

<button>{t('toolbar.save')}</button>

negotiateLocale picks the best language you have for what the browser asks, such as nl for a browser set to nl-BE, within the script it’s written in: zh-TW doesn’t get zh-CN, since Taiwan writes Traditional Chinese and zh-CN is Simplified. A language in loaders is loaded the first time someone switches to it, so your bundle carries only the one you start with.

Strings#

A language’s translations are your strings, grouped however you like:

export default {
  toolbar: { save: 'Opslaan', zoomIn: 'Inzoomen' },
  search: {
    results: { one: '{count} resultaat', other: '{count} resultaten' },
  },
};
t('toolbar.save'); // 'Opslaan'
t('search.results', { params: { count: 3 } }); // '3 resultaten'
  • {name} is filled in from params.
  • Plurals: a string with one and other picks by params.count.
  • Missing strings fall back to the fallback language (English unless you set another), then to fallback if you pass one, then to the key itself. You get a warning in the console once, so you notice.

Switch the language#

language-picker.svelte
<script lang="ts">
  import { useI18n, useI18nState } from '@embedpdf/svelte/i18n';

  const i18n = useI18n();
  const state = useI18nState();
</script>

<select
  value={state.locale}
  disabled={state.loading !== null}
  onchange={(event) => i18n.setLocale(event.currentTarget.value)}
>
  {#each state.locales as language (language.code)}
    <option value={language.code}>{language.name}</option>
  {/each}
</select>

Loading live preview…

<script lang="ts" module>
  import { DocumentGate, Viewer, type OpenInput } from '@embedpdf/svelte/runtime';
  import { RenderLayer, renderPlugin } from '@embedpdf/svelte/render';
  import { Stage, stagePlugin } from '@embedpdf/svelte/stage';
  import { commandsPlugin, standardCommands } from '@embedpdf/svelte/commands';
  import { i18nPlugin, type Locale } from '@embedpdf/svelte/i18n';
  import { localEngine } from '@embedpdf/engine';
  import CommandButton from './CommandButton.svelte';
  import LanguagePicker from './LanguagePicker.svelte';
  import PageCount from './PageCount.svelte';

  import '../basic.css';

  const engine = localEngine();

  // Your strings. The buttons' labels are the standard commands', which come translated.
  const en: Locale = {
    code: 'en',
    name: 'English',
    translations: {
      app: { language: 'Language', pages: { one: '{count} page', other: '{count} pages' } },
    },
  };
  const nl: Locale = {
    code: 'nl',
    name: 'Nederlands',
    translations: {
      app: { language: 'Taal', pages: { one: '{count} pagina', other: "{count} pagina's" } },
    },
  };

  const plugins = [
    stagePlugin(),
    renderPlugin(),
    commandsPlugin({ commands: standardCommands }),
    i18nPlugin({
      locale: 'nl',
      locales: [en, nl],
      // Loaded the first time someone picks them; in your app, `() => import('./locales/de')`.
      loaders: {
        de: async () => ({
          code: 'de',
          name: 'Deutsch',
          translations: {
            app: { language: 'Sprache', pages: { one: '{count} Seite', other: '{count} Seiten' } },
          },
        }),
        ja: async () => ({
          code: 'ja',
          name: '日本語',
          translations: { app: { language: '言語', pages: { other: '{count} ページ' } } },
        }),
      },
    }),
  ];

  const ebook = async (): Promise<OpenInput> => {
    const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
    return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
  };
</script>

<Viewer {engine} {plugins} initialDocuments={[{ source: ebook }]}>
  <DocumentGate>
    {#snippet fallback()}
      <p class="loading">…</p>
    {/snippet}
    <div class="toolbar">
      <LanguagePicker />
      <CommandButton id="page:previous" />
      <CommandButton id="page:next" />
      <CommandButton id="zoom:in" />
      <CommandButton id="document:print" />
      <PageCount />
    </div>
    <Stage class="stage">
      <RenderLayer />
    </Stage>
  </DocumentGate>
</Viewer>

setLocale() loads the language if it needs to, then switches. Everything that uses useT() updates in the new language.

Add strings later#

For a plugin of your own, or strings from your server, add them to a language while the app runs:

i18n.addTranslations('nl', { review: { approve: 'Goedkeuren' } });
i18n.addTranslations('nl', { 'review.reject': 'Afwijzen' }); // a dotted key works too

registerLocale() adds a whole new language, and returns a function that removes it again.

EmbedPDF’s own text#

Commands have labels, and the standard commands come translated in English, Dutch, German, French, Spanish, Chinese, Swedish and Japanese, the languages of the standard stamps. Change any of them with addTranslations, under the same keys.

Right-to-left languages#

A language can say it reads right to left with direction: 'rtl'. direction in State tells you, so you can set it on your viewer’s root:

direction.svelte
<script lang="ts">
  import { useI18nState } from '@embedpdf/svelte/i18n';

  const state = useI18nState();
</script>

<div dir={state.direction}>
  <!-- your viewer -->
</div>

Loading live preview…

<script lang="ts" module>
  import { DocumentGate, Viewer, type OpenInput } from '@embedpdf/svelte/runtime';
  import { renderPlugin } from '@embedpdf/svelte/render';
  import { stagePlugin } from '@embedpdf/svelte/stage';
  import { commandsPlugin, standardCommands } from '@embedpdf/svelte/commands';
  import { i18nPlugin, type Locale } from '@embedpdf/svelte/i18n';
  import { localEngine } from '@embedpdf/engine';
  import ViewerRoot from './ViewerRoot.svelte';

  import '../rtl.css';

  const engine = localEngine();

  const en: Locale = {
    code: 'en',
    name: 'English',
    translations: { app: { title: 'The ebook', switchTo: 'العربية' } },
  };
  // Arabic reads right to left. The standard commands have no Arabic labels, so the app gives
  // them, under the same keys.
  const ar: Locale = {
    code: 'ar',
    name: 'العربية',
    direction: 'rtl',
    translations: {
      app: { title: 'الكتاب الإلكتروني', switchTo: 'English' },
      commands: {
        page: { previous: 'الصفحة السابقة', next: 'الصفحة التالية' },
        zoom: { in: 'تكبير', out: 'تصغير' },
      },
    },
  };

  const plugins = [
    stagePlugin(),
    renderPlugin(),
    commandsPlugin({ commands: standardCommands }),
    i18nPlugin({ locale: 'ar', locales: [en, ar] }),
  ];

  const ebook = async (): Promise<OpenInput> => {
    const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
    return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
  };
</script>

<Viewer {engine} {plugins} initialDocuments={[{ source: ebook }]}>
  <DocumentGate>
    {#snippet fallback()}
      <p class="loading">…</p>
    {/snippet}
    <ViewerRoot />
  </DocumentGate>
</Viewer>

For documents that read right to left, such as a book in Arabic, set the Stage’s direction too, so pages and spreads run from right to left.

Methods#

Everything useI18n() gives you.

MethodWhat it does
t(key, { params?, fallback? })A string in the current language, with params filled in. In a component, use useT(), which updates when the language changes. Strings
getTranslator()A t function that stays the same until the language or the strings change, for code that caches on it. useT() returns it.
hasKey(key)Whether a key has a string in the current or the fallback language. Never warns.
setLocale(code)Loads the language if it needs to, then switches. Resolves once it shows; rejects when it can’t load, or when a newer call replaces it. Switch the language
addTranslations(code, translations)Adds strings to one of your languages, loaded or not, and replaces the ones with the same key. Add strings later
registerLocale(locale)Adds a whole language, { code, name, direction?, translations }, and returns a function that removes it again
getSettings(), updateSettings(changes), resetSettings()Reads the settings, changes them while the app runs, or goes back to what you registered. In a component, use useI18nSettings(). Settings

Every method that returns a promise also takes a signal in its options, to cancel it.

State#

Everything useI18nState() gives you, as one reactive object: read its fields, and your markup updates when they change. The method in the last column reads the same value once.

FieldWhat it isRead once
localeThe current language’s codegetLocale()
direction'ltr' or 'rtl', how the current language readsgetDirection()
localesEvery language you can switch to, loaded or not, each with code, name, direction and loaded. A language in loaders shows its code as its name until it’s loaded.listLocales()
loadingThe code of the language being loaded, or nullgetLoadingLocale()

Settings#

Set them when you register the plugin, and change them while the app runs with i18n.updateSettings({ … }); i18n.resetSettings() goes back to what you registered, and useI18nSettings() gives you the current settings.

SettingWhat it doesDefault
localeThe language to start withfallbackLocale
fallbackLocaleThe language to use for strings another language misses'en'
localesLanguages loaded from the startnone
loadersLanguages loaded when someone switches to themnone

Events#

locale-events.svelte
<script lang="ts">
  import { useI18nEvent } from '@embedpdf/svelte/i18n';

  useI18nEvent(
    (i18n) => i18n.onLocaleChanged,
    ({ locale }) => document.documentElement.setAttribute('lang', locale),
  );
</script>

<!-- your toolbar and pages -->
EventFires whenCarries
onLocaleChangedThe language changedlocale, previousLocale
onLocaleLoadFailedA language couldn’t be loadedlocale, error
onSettingsChangedThe settings changedsettings, changed

Good to know#

Before a document opens#

The plugin needs no engine and no document, so your loading screen, a password prompt and an empty viewer can be translated too.

A language that fails to load#

When a language in loaders can’t be loaded, for example because the user is offline, the viewer stays in the language it had, and onLocaleLoadFailed tells you why. Call setLocale() again to try once more.

Was this page helpful?

Your feedback goes directly to the documentation team.