Docs

Documentation versions (currently viewingVaadin 25.4 (pre-release))

Localization in React Views

How React views load translations, translate strings, and change the language.

React views translate strings in the browser with the @vaadin/hilla-react-i18n package. They use the same translation files as Flow views: the browser loads the translations from the server, which reads them with the I18NProvider of the application. For where to put the translation files, see Localization.

Loading Translations

Call i18n.configure() once, before the views translate anything. For example, call it at the top level of the main layout:

Source code
views/@layout.tsx

The method chooses the initial language and loads its translations. It returns a Promise that resolves when the translations are loaded. Awaiting it at the top level of the layout, as above, delays the views until then. The initial language is the first of these that’s set:

  1. The language option, such as i18n.configure({ language: 'de' }).

  2. The language that the browser last set with setLanguage(). It’s stored in a cookie.

  3. The language of the browser.

The i18n.initialized signal becomes true when the translations are loaded. A view that doesn’t wait for configure() can use it to show a placeholder until then.

Translating Strings

The translate() function returns the translation of a key in the current language. Mark the key with the key tag: translate() accepts only keys marked with it.

Source code
orders-view.tsx

A component that calls translate() renders again when the translations change, for example after a change of language. Effects and computed signals that call it also run again.

When the translations have no value for the key, translate() returns the key after an exclamation mark and the language, such as !de: orders.count. The default I18N provider of Flow views marks a missing translation the same way.

Parameters

React views format the translations with the International Components for Unicode (ICU) message format. A translation can contain placeholders, plural and select forms, and number and date formats. Pass the values of the placeholders as an object, with a property for each placeholder name, such as { count } in the example above:

Source code
translations.properties
orders.count=You have {count, plural, one {# order} other {# orders}}.

Flow views format translations with Java’s MessageFormat, which numbers its placeholders, such as {0}. In a React view, pass the value of such a placeholder as { 0: value }.

Translating View Titles and Menu Items

The title of a view and the title of its menu item can be translation keys. Mark them with the key tag in the view configuration:

Source code
views/orders.tsx

useViewConfig() and createMenuItems() return these titles as they are. Translate them with i18n.translateDynamic(), which takes any string and returns a signal with its translation. A signal in JSX renders as its value:

Source code
views/@layout.tsx

When the translations don’t have the key yet, the signal is an empty string, and the browser requests the key from the server. It then logs a warning in the browser console. The menu also includes the Flow views that have the @Menu annotation, so give them translation keys as titles as well. Otherwise, their titles don’t appear as written.

For more about the view configuration and the menu, see View Configuration.

Changing the Language

i18n.setLanguage() changes the language. It loads the translations for the new language, and the views that use them render again. It also stores the language in a cookie, so that configure() uses it the next time the application loads.

Source code
tsx
<Button onClick={() => i18n.setLanguage('de')}>Deutsch</Button>

The i18n.language signal holds the current language. A language is a standard language tag, such as de or de-DE.

The server chooses the translation of each key from the most specific file that has it. For de-DE, it looks in translations_de_DE.properties, then translations_de.properties, and then translations.properties.

The language of React views is separate from the locale of Flow views. setLanguage() doesn’t change the locale of the UI, and UI.setLocale() doesn’t change the language of React views.

Development and Production Mode

In development mode, the browser loads all the translations of the language. With live reload, a change to a translation file reaches the browser without a page reload.

A production build records the keys that each JavaScript chunk of the application uses. The browser then loads only the translations of the chunks that it has loaded:

  • A key in a translate() call is loaded with the chunk that contains the call.

  • A key elsewhere, such as in a view configuration, is loaded when the application starts.

The build finds only keys written as a key tag with literal text. When i18n.translateDynamic() gets a key that the build didn’t find, such as a key computed at runtime, the browser requests it from the server and logs a warning.

Custom I18N Providers

React views get their translations through two methods of the I18NProvider:

getAllTranslations(Locale)

Returns all the translations of a locale. Used in development mode. The default implementation returns no translations, so a custom provider must implement it for React views to get translations in development mode.

getTranslations(Collection, Locale)

Returns the translations of the given keys. Used in production mode, and for the keys that i18n.translateDynamic() requests. The default implementation calls getTranslation() for each key.