|
| 1 | +--- |
| 2 | +title: Internationalization (I18n) |
| 3 | +page-title: Adding Internationalization (I18n) to Hilla Applications |
| 4 | +description: Enable multilingual support in Hilla applications by configuring and using the Hilla i18n system. |
| 5 | +meta-description: Guide to setting up and using internationalization (i18n) with Hilla applications. |
| 6 | +order: 140 |
| 7 | +--- |
| 8 | + |
| 9 | += [since:com.vaadin:vaadin@V24.6]#Internationalization (I18n) Support# |
| 10 | + |
| 11 | +You can add internationalization (i18n) support to your Hilla applications using the `@vaadin/hilla-react-i18n` package. This guide walks you through setting up and using the feature effectively to create multilingual user interfaces. |
| 12 | + |
| 13 | +== Add and Configure `i18n` |
| 14 | + |
| 15 | +First, import the `i18n` module and call the `configure` method during your application setup or in your main view initialization. |
| 16 | + |
| 17 | +[source,typescript] |
| 18 | +---- |
| 19 | +import { effect } from '@vaadin/hilla-react-signals'; |
| 20 | +import { i18n } from '@vaadin/hilla-react-i18n'; |
| 21 | +
|
| 22 | +effect(() => { |
| 23 | + i18n.configure(); |
| 24 | +}); |
| 25 | +---- |
| 26 | + |
| 27 | +=== Behavior Details |
| 28 | + |
| 29 | +By default, the system uses the browser's language (`navigator.language`). If a user has previously selected a language, it is remembered in a cookie. You can also explicitly configure the language during setup: |
| 30 | + |
| 31 | +[source,typescript] |
| 32 | +---- |
| 33 | +i18n.configure({ language: 'de-DE' }); |
| 34 | +---- |
| 35 | + |
| 36 | +Configuration marks the system as initialized, allowing your UI to react accordingly. |
| 37 | + |
| 38 | +== Create and Place Translation Files |
| 39 | + |
| 40 | +Translation files must be placed in the following file: |
| 41 | + |
| 42 | +---- |
| 43 | +src/main/resources/vaadin-i18n/translations.properties |
| 44 | +---- |
| 45 | + |
| 46 | +These files follow the standard Java `.properties` file format, where each line contains a key-value pair separated by an equals sign (`=`). Keys represent the translation identifiers, and values are the corresponding translations. |
| 47 | + |
| 48 | +Example of a `translations.properties` file for English: |
| 49 | + |
| 50 | +---- |
| 51 | +addresses.form.city.label=City |
| 52 | +addresses.form.street.label=Street |
| 53 | +---- |
| 54 | + |
| 55 | +To add support for other languages, create additional `.properties` files with the language code appended to the filename. For example, to add German translations, create a file named `translations_de.properties`: |
| 56 | + |
| 57 | +---- |
| 58 | +addresses.form.city.label=Stadt |
| 59 | +addresses.form.street.label=Straße |
| 60 | +---- |
| 61 | + |
| 62 | +The system automatically selects the appropriate file based on the active language. Language codes are structured to represent the language and optional regional variations, such as `en` for English or `en-US` for American English. |
| 63 | + |
| 64 | +If a translation is missing in the most specific file (e.g., `translations_de_DE.properties`), the system gracefully falls back to a less specific file (e.g., `translations_de.properties`). If no match is found, it defaults to the base file (`translations.properties`), ensuring the application remains functional even when translations are incomplete. |
| 65 | + |
| 66 | +== Use the `translate` Function |
| 67 | + |
| 68 | +The `translate` function retrieves translated strings based on the active language. Those strings must be marked with the `key` tag, so that Hilla can identify them and include in the right chunk when building the application for production. |
| 69 | + |
| 70 | +[source,tsx] |
| 71 | +---- |
| 72 | +import { key, translate } from '@vaadin/hilla-react-i18n'; |
| 73 | +
|
| 74 | +return <div>{translate(key`addresses.form.city.label`)}</div>; |
| 75 | +---- |
| 76 | + |
| 77 | +If a translation is missing, the key is shown as-is. |
| 78 | + |
| 79 | +== React Integration |
| 80 | + |
| 81 | +i18n is deeply integrated into the reactive programming model of Hilla. Components automatically update when the language changes. Signal-based reactivity (`useSignalEffect`, `useComputed`) works seamlessly. |
| 82 | + |
| 83 | +Example using computed signals: |
| 84 | + |
| 85 | +[source,tsx] |
| 86 | +---- |
| 87 | +import { useComputed } from '@vaadin/hilla-react-signals'; |
| 88 | +import { key, translate } from '@vaadin/hilla-react-i18n'; |
| 89 | +
|
| 90 | +function OrderSummary({ itemCount }: { itemCount: number }) { |
| 91 | + const orderMessage = useComputed(() => { |
| 92 | + if (itemCount === 0) { |
| 93 | + return translate(key`order.empty`); |
| 94 | + } else { |
| 95 | + return translate(key`order.details`, { count: itemCount }); |
| 96 | + } |
| 97 | + }); |
| 98 | +
|
| 99 | + return <div>{orderMessage.value}</div>; |
| 100 | +} |
| 101 | +---- |
| 102 | + |
| 103 | +You can also show placeholders before i18n is initialized: |
| 104 | + |
| 105 | +[source,tsx] |
| 106 | +---- |
| 107 | +{i18n.initialized.value ? <ActualContent /> : <LoadingSpinner />} |
| 108 | +---- |
| 109 | + |
| 110 | +== File Router Integration |
| 111 | + |
| 112 | +The file router allows you to define a <<routing#customizing-routes,configuration>> for each view. This configuration contains elements that are good targets for translation, such as the page title and the menu link title. |
| 113 | + |
| 114 | +Because translation keys need to be discoverable by the build system, use the `key` tag to mark these elements: |
| 115 | + |
| 116 | +[source,tsx] |
| 117 | +.`src/main/frontend/views/about.tsx` |
| 118 | +---- |
| 119 | +import type { ViewConfig } from '@vaadin/hilla-file-router/types.js'; |
| 120 | +import { key } from '@vaadin/hilla-react-i18n'; |
| 121 | +
|
| 122 | +export default function AboutView() { |
| 123 | + return ( |
| 124 | + /* ... */ |
| 125 | + ); |
| 126 | +} |
| 127 | +
|
| 128 | +export const config: ViewConfig = { |
| 129 | + title: key`about.title`, |
| 130 | + menu: { |
| 131 | + title: key`about.menuTitle`, |
| 132 | + }, |
| 133 | +}; |
| 134 | +---- |
| 135 | + |
| 136 | +When creating the menu, use the `translateDynamic` function to retrieve translated values. Unlike `translate`, `translateDynamic` does not require the `key` tag and returns a `Signal` instead of a string. |
| 137 | + |
| 138 | +[source,tsx] |
| 139 | +---- |
| 140 | +import { i18n } from '@vaadin/hilla-react-i18n'; |
| 141 | +
|
| 142 | +{createMenuItems().map(({ to, title, icon }) => ( |
| 143 | + <SideNavItem path={to} key={to}> |
| 144 | + {icon ? <Icon src={icon} slot="prefix"></Icon> : <></>} |
| 145 | + {i18n.translateDynamic(title)} |
| 146 | + </SideNavItem> |
| 147 | +))} |
| 148 | +---- |
| 149 | + |
| 150 | +A similar approach can be used for the page title. |
| 151 | + |
| 152 | +If the value passed to `translateDynamic` is not a translation key, a server call is made to retrieve the translation. To avoid performance issues, use `translateDynamic` only with known keys. If the received string is a key, `translateDynamic` behaves like `translate` and returns the translation efficiently. |
| 153 | + |
| 154 | +== Dynamically Changing the Language |
| 155 | + |
| 156 | +You can switch the language at runtime to adapt to user preferences. |
| 157 | + |
| 158 | +[source,typescript] |
| 159 | +---- |
| 160 | +i18n.setLanguage('de-DE'); |
| 161 | +---- |
| 162 | + |
| 163 | +== ICU Message Format Support |
| 164 | + |
| 165 | +Hilla's i18n system supports the International Components for Unicode (ICU) Message Format, enabling advanced translation scenarios like pluralization, selection, and number/date formatting. |
| 166 | + |
| 167 | +Example in `translations.properties`: |
| 168 | + |
| 169 | +[source,properties] |
| 170 | +---- |
| 171 | +messages.count=You have {count, plural, one {# message} other {# messages}}. |
| 172 | +---- |
| 173 | + |
| 174 | +Usage example: |
| 175 | + |
| 176 | +[source,typescript] |
| 177 | +---- |
| 178 | +translate(key`messages.count`, { count: 5 }); // Output: "You have 5 messages." |
| 179 | +---- |
| 180 | + |
| 181 | +Supported ICU features include: |
| 182 | + |
| 183 | +- dynamic number and date formatting; |
| 184 | +- plural forms; |
| 185 | +- gender and value-based selections; |
| 186 | +- escaping special characters. |
| 187 | + |
| 188 | +== Hot Module Replacement (HMR) in Development |
| 189 | + |
| 190 | +During development, translation files update automatically through Hot Module Replacement (HMR). No manual reload is needed: when translations change, they are automatically fetched and applied. |
| 191 | + |
| 192 | +With these tools, building responsive and adaptable multilingual applications with Hilla becomes intuitive and efficient. |
0 commit comments