This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/web/features/localization.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Localization: Change languages in JavaScript PDF viewer | Nutrient

Nutrient Web SDK comes with many built-in languages:

  • Chinese Simplified/Chinese Traditional (zh-Hans/zh-Hant)
  • Croatian (hr)
  • Czech (cs)
  • Danish (da)
  • Dutch (nl)
  • English (en)
  • English United Kingdom (en-GB)
  • Finnish (fi)
  • French (fr)
  • French Canada (fr-CA)
  • German (de)
  • Greek (el)
  • Indonesian (id)
  • Italian (it)
  • Japanese (ja)
  • Korean (ko)
  • Malay (ms)
  • Norwegian Bokmål (nb-NO)
  • Polish (pl)
  • Portuguese Brazil/Portugal (pt/pt-PT)
  • Russian (ru)
  • Slovak (sk)
  • Slovenian (sl)
  • Spanish (es)
  • Swedish (sv)
  • Thai (th)
  • Turkish (tr)
  • Ukrainian (uk)
  • Welsh (cy)

Adding additional localization to Nutrient Web SDK

You can add new locales to Nutrient Web SDK.

NutrientViewer.I18n consists of two parts, which the localization engine uses when translating:

In TypeScript, Nutrient Web SDK 1.22 and later declares the locale keys on NutrientViewer.I18n.messages as optional, because a locale is only present after it’s loaded. Guard the locale entry, or use a non-null assertion after calling preloadLocalizationData().

Since Nutrient Web SDK 2020.1, you don’t need to add pluralization and formatting rules, because the SDK uses the native Intl API(opens in a new tab) instead. react-intl(opens in a new tab) v3 introduced this change.

Browsers that don’t support the Intl API(opens in a new tab) (like Safari 13 and below) need a polyfill for Intl.PluralRules. They also need an individual polyfill for each supported locale. To keep supporting these browsers when you provide additional locales, include the corresponding locale polyfill:

// Add the locale to the locales list (Wolof language).
NutrientViewer.I18n.locales.push("wo");
// Is this browser using the `Intl.PluralRules` polyfill?
if (Intl.PluralRules.polyfilled) {
// Then include the plural rules locale data polyfill.
await import("@formatjs/intl-pluralrules/dist/locale-data/wo");
}
// Add Wolof translations for messages.
NutrientViewer.I18n.messages["wo"] = wolofMessages;
// Change current language to Wolof.
instance.setLocale("wo");

Install the locale data polyfills into your local node_modules folder with npm:

npm i @formatjs/intl-pluralrules

This change deprecates NutrientViewer.I18n.localizationData, which added localization data before Nutrient Web SDK 2020.1.

Let’s say we want to add support for French. (Note that this is just an example because Nutrient Web SDK already supports French.) To do this, we have to add the information about French to the two objects above, as shown below.

  1. Push the new locale to the NutrientViewer.I18n.locales array:

    NutrientViewer.I18n.locales.push("fr");
  2. Add the translated messages to the NutrientViewer.I18n.messages object:

    NutrientViewer.I18n.messages.fr = {
    searchNextMatch: "Prochain",
    // Other translations here.
    };

    You can use the English messages object (NutrientViewer.I18n.messages.en) as a template.

  3. Optionally, add the polyfill for browsers not supporting the Intl API:

    // Is this browser using the `Intl.PluralRules` polyfill?
    if (Intl.PluralRules.polyfilled) {
    // Then include the plural rules locale data polyfill.
    await import("@formatjs/intl-pluralrules/dist/locale-data/fr");
    }

To add locale data for a new language:

  • Follow the download instructions on the react-intl page(opens in a new tab).
  • Load react-intl/locale-data/YourNewLocaleSymbol (the locale data) either via the script tag or import.
  • Optionally, add the localization data polyfill for browsers not supporting the Intl API.

Your application now supports French.

Note that we don’t store your information. As such, you need to persist translations in your data store of choice.

Customizing existing translations

Once the SDK loads localization data, all the translated messages are available on the NutrientViewer.I18n.messages object.

Simple customization

To customize a string for a specific locale, mutate this object directly:

console.log(NutrientViewer.I18n.messages.en.searchNextMatch);
// > "Next"
NutrientViewer.I18n.messages.en.searchNextMatch =
"Go to the next search result.";
console.log(NutrientViewer.I18n.messages.en.searchNextMatch);
// > "Go to the next search result."

Overriding multiple translations

By default, translations are only available after you create an instance of Nutrient Web SDK. To modify translations before creating an instance, use the NutrientViewer.I18n.preloadLocalizationData method:

await NutrientViewer.I18n.preloadLocalizationData("en");
NutrientViewer.I18n.messages.en.searchNextMatch =
"Go to the next search result.";
// ...
NutrientViewer.load({
locale: "en",
// Other configuration options.
});

In TypeScript, add a guard or a non-null assertion after preloading:

await NutrientViewer.I18n.preloadLocalizationData("en");
NutrientViewer.I18n.messages.en!.searchNextMatch =
"Go to the next search result.";

Nutrient Web SDK 1.22 and later also exposes the Locale, MessageId, LocaleMessages, and LocaleMessageOverrides types, which enable autocompletion of shipped locale tags and message IDs.

To override multiple translations at once, use the spread operator to merge your custom text with existing translations:

NutrientViewer.I18n.preloadLocalizationData("en").then(() => {
NutrientViewer.I18n.messages.en = {
...NutrientViewer.I18n.messages.en,
loading: "Custom loading text",
annotations: "Custom annotations label",
searchNextMatch: "Go to the next result",
// Add more custom translations here.
};
});
NutrientViewer.load({
locale: "en",
// Other configuration options.
}).then((instance) => {
console.log("Nutrient loaded with custom translations!");
});

If NutrientViewer.I18n.preloadLocalizationData throws an error like PSPDFKitError: I18n: Cannot find translations for "en", pass the baseUrl option as the second parameter:

NutrientViewer.I18n.preloadLocalizationData("en", {
baseUrl: "path/to/your/assets",
});

The baseUrl option ensures the SDK loads the localization files from the correct location.

Messages with placeholders and plurals

Messages on NutrientViewer.I18n.messages are ICU MessageFormat(opens in a new tab) strings. Follow these rules when overriding a message with placeholders or plural rules:

  • Use only the placeholders the default message uses — for example, {arg0} — and keep their names.
  • Match every { with exactly one }.

Log the default message to see its placeholders. The annotation count in the annotations sidebar, for example, uses annotationsCount:

await NutrientViewer.I18n.preloadLocalizationData("en");
console.log(NutrientViewer.I18n.messages.en.annotationsCount);
// > {arg0, plural,
// > =0 {No Annotations}
// > one {{arg0} Annotation}
// > other {{arg0} Annotations}
// > }

This override keeps the arg0 plural and changes the text of each branch. Inside a branch, # stands for the number:

await NutrientViewer.I18n.preloadLocalizationData("en");
NutrientViewer.I18n.messages.en.annotationsCount =
"{arg0, plural, =0 {No annotations yet} one {# annotation} other {# annotations}}";

With zero, one, and three annotations, the sidebar header shows “No annotations yet,” “1 annotation,” and “3 annotations.” With zero annotations, the empty list below the header shows a second message, noAnnotations, which reads “No Annotations” by default. Override noAnnotations as well so the header and the empty list match. In English, match a count of zero with =0, not zero.

In Nutrient Web SDK 1.23 and later, the SDK ignores an override it fails to format, and the label shows the default English text. A missing } causes this, as does an extra } between the plural’s branches. So does a plural on a placeholder the message doesn’t have, such as {count} instead of {arg0}. The console logs a warning that names the message:

[Nutrient Web SDK] The message "annotationsCount" for locale "en" could not be formatted. If you customized this message, fix NutrientViewer.I18n.messages["en"]["annotationsCount"] — see https://formatjs.github.io/docs/core-concepts/icu-syntax/

Nutrient Web SDK 1.21 also shows the default English text, but without logging a warning. Nutrient Web SDK 1.20 and 1.22 display the raw ICU message text in the label instead, such as {arg0, plural, =0 {No Annotations} one {{arg0} Annotation} other {{arg0} Annotations} }.

A } outside the plural block, such as one after its closing brace, isn’t a formatting error. The label shows it as literal text, and the SDK logs no warning.

Nutrient Web SDK 1.21 renamed the placeholders of five messages. If you override any of them, refer to the 1.21 release notes.

Forcing a specific language

By default, Nutrient Web SDK tries to detect the language using the navigator.language value. However, set the locale programmatically before loading Nutrient Web SDK with the locale configuration option:

NutrientViewer.load({
locale: "de",
// Other configuration options.
});

Change the locale at runtime with the Instance#setLocale instance method:

await instance.setLocale("de");
console.log("Successfully changed the language to German.");