Skip to content

Commit 0104570

Browse files
authored
(hilla) documentation for Internationalization (i18n) (#4278)
* First document version * Add `key` and `translateDynamic` * Address Vale * Address Vale (again) * Change acronym definition format
1 parent b5848b1 commit 0104570

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed

articles/hilla/guides/i18n.adoc

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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

Comments
 (0)