Internationalization (i18n)
Mateu passes every user-visible string — field labels, page titles, validation messages, button texts, alerts — through a bean implementing the Translator interface. This centralizes translation in a single extension point.
The Translator interface
Section titled “The Translator interface”public interface Translator { String translate(String text, HttpRequest httpRequest);}| Parameter | Description |
|---|---|
text | The original string (as it appears in code or in annotations) |
httpRequest | Request context — lets you read the Accept-Language header, session cookies, or anything else needed to resolve the locale |
The returned value is the string sent to the frontend.
Default implementation
Section titled “Default implementation”The core module ships an implementation based on standard Java i18n (ResourceBundle). To use it, just add a messages file to the classpath:
src/main/resources/ messages.properties ← default locale messages_es.properties ← Spanish messages_fr.properties ← French ...Example messages_es.properties:
Name=NombreSave=GuardarOrder\ #=Número de pedidoField\ is\ required=El campo es obligatorioThe locale is resolved from the Accept-Language header of the HTTP request.
Providing an alternative implementation
Section titled “Providing an alternative implementation”To replace the default implementation, register a bean implementing Translator with higher priority. In Spring Boot:
@Component@Primarypublic class DatabaseTranslator implements Translator {
final TranslationRepository repo;
@Override public String translate(String text, HttpRequest httpRequest) { String locale = httpRequest.getHeaderValue("Accept-Language"); return repo.findTranslation(text, locale) .orElse(text); // fall back to the original text }}In Quarkus or Micronaut use the equivalent annotation (@Alternative + @Priority, @Primary, etc.).
Common scenarios
Section titled “Common scenarios”| Scenario | Suggested implementation |
|---|---|
Translations in .properties files | Default implementation from core |
| Translations in a database (hot-editable) | @Primary bean querying the DB |
| Per-tenant / white-label labels | Read the tenant from the request and resolve the matching bundle |
| External translation service | API call with a local cache to keep latency low |
| Development mode without translations | Return text directly (pass-through) |
Performance considerations
Section titled “Performance considerations”Your Translator implementation is invoked for every string of every response. If resolution involves DB or network access, add a cache layer keyed by (text, locale):
@Component@Primarypublic class CachedDatabaseTranslator implements Translator {
final TranslationRepository repo; final Cache<String, String> cache = Caffeine.newBuilder().maximumSize(10_000).build();
@Override public String translate(String text, HttpRequest httpRequest) { String locale = resolveLocale(httpRequest); return cache.get(text + "|" + locale, k -> repo.findTranslation(text, locale).orElse(text)); }}Related
Section titled “Related”- Security — authentication and role-based authorization
- Key interfaces — full reference of public interfaces