EmbedPDF

Translations

The viewer speaks the user’s language: it picks the best match from the browser’s languages. To choose one yourself, set locale:

{ locale: 'de' }

locale follows its prop, so a language switch in your app switches the viewer too. Without it, or with 'auto', the viewer follows the browser.

The languages it speaks#

English, German, French, Spanish, Italian, Dutch, Portuguese, Polish, Swedish, Japanese, Chinese (Simplified and Traditional), Korean, Arabic and Hebrew. Each one loads only when it’s needed. For Arabic and Hebrew, the viewer is right-to-left: the bars mirror, and start and end swap sides.

A language matches in its own script: readers in Taiwan and Hong Kong (zh-TW, zh-HK) see Traditional Chinese, and readers in China and Singapore (zh-CN, zh-SG) Simplified.

Change a word#

A built-in button’s label has the command’s id as its key. Give a new one, per language:

{
  strings: {
    en: { 'document:download': 'Save a copy' },
    de: { 'document:download': 'Kopie speichern' },
  },
}

A panel’s button and the title in its header are one word, under the button’s command id: 'panel:comments' renames both. See Panels.

A mode tab is a button too, with the command mode:annotate for Annotate. Here a picker in your app sets locale, and strings gives two labels your own words in each language it offers:

Loading live preview…

import { useState } from 'react';
import { PDFViewer } from '@embedpdf/viewer-react';

import './languages.css';

const welcome = '/pdfs/welcome.pdf';

// Your own words for two of the viewer's labels, in each language.
const strings = {
  en: { 'mode:annotate': 'Review', 'document:download': 'Save a copy' },
  de: { 'mode:annotate': 'Prüfen', 'document:download': 'Kopie speichern' },
  fr: { 'mode:annotate': 'Relire', 'document:download': 'Enregistrer une copie' },
  nl: { 'mode:annotate': 'Nakijken', 'document:download': 'Kopie opslaan' },
  ar: { 'mode:annotate': 'مراجعة', 'document:download': 'حفظ نسخة' },
};

export default function App() {
  const [locale, setLocale] = useState('en');
  return (
    <div className="app">
      <header className="bar">
        <select
          aria-label="Language"
          value={locale}
          onChange={(event) => setLocale(event.target.value)}
        >
          <option value="en">English</option>
          <option value="de">Deutsch</option>
          <option value="fr">Français</option>
          <option value="nl">Nederlands</option>
          <option value="ar">العربية</option>
        </select>
      </header>
      <PDFViewer className="viewer" src={welcome} strings={strings} locale={locale} />
    </div>
  );
}

Try it: the second tab reads Review instead of Annotate, and the menu at the top left has Save a copy. Pick Deutsch above the viewer: every word changes, yours too. Pick العربية and the bars mirror.

Translate your own buttons#

Give a command a labelKey instead of a label, and its words per language:

{
  commands: [{ id: 'acme:send', labelKey: 'acme.send', icon: 'signature', run: send }],
  strings: {
    en: { 'acme.send': 'Send for signature' },
    nl: { 'acme.send': 'Verstuur ter ondertekening' },
  },
}

Panels and mode bars take a titleKey the same way, for their title. A panel’s title is its button’s label too.

The style panel’s words have keys too: each property and option by what it says (annotation.style.stroke, annotation.style.opacity, annotation.lineEndings.openArrow), and a form field’s settings by theirs (form.settings.tooltip). Change one like any other word:

{ strings: { en: { 'annotation.style.stroke': 'Line' } } }

So do the page number’s words, under pageNumber., and the comments panel’s, under comments.. The ones you’re most likely to change:

KeyWhat it says
pageNumber.page{current} / {total}
pageNumber.pages{first}–{last} / {total}, while a spread fits across the view (spread)
pageNumber.goToPageThe tooltip of the page number, and the label of the field it turns into
comments.empty, comments.emptyReadOnlyWhat the panel says while the document has no comments, when the user can add some and when not
comments.addPlaceholder, comments.replyPlaceholder, comments.replyThe field for a new comment, and for a reply
comments.statusAccepted, comments.statusRejected, comments.statusCancelled, comments.statusCompleted, comments.statusNoneA thread’s status

A word in braces is filled in as the viewer writes it: keep it in your words. The number that turns into a field to type a page is {current}, or {first}:

{ strings: { en: { 'pageNumber.page': 'Page {current} of {total}', 'comments.empty': 'No notes yet.' } } }

A language of your own#

Give strings for a language the viewer doesn’t have, and it becomes one of its languages. Anything you don’t translate shows in English:

{
  locale: 'da',
  strings: { da: { 'document:download': 'Hent', 'zoom:in': 'Zoom ind' } },
}

It reads right to left when its code says so: a right-to-left language such as fa or ur, or a right-to-left script such as ku-Arab.

Good to know#

Change the language from code#

viewer.get('i18n').setLocale('fr') does the same as the locale prop, and returns a promise that resolves once the language has loaded.

Add words while the app runs#

viewer.get('i18n').addTranslations('nl', { … }) adds or replaces words in a language without starting over, for words that arrive from your own translation service.

Labels in the toolbar#

The toolbar measures the words it shows, so a longer translation moves fewer or more buttons into the “More” menu by itself.

Was this page helpful?

Your feedback goes directly to the documentation team.