Neuigkeiten von trion.
Immer gut informiert.

Spring Boot API Lokalisierung

Typischerweise sind APIs zur Verarbeitung durch andere Systeme gedacht. Wenn die Ausgaben jedoch für Menschen bestimmt sind, kann es sinnvoll sein, diese zu lokalisieren.
Das Spring Framework bringt dazu bereits Bausteine mit, ebenso Spring WebMVC für die Web bzw. API-Schicht. Am Beispiel einer Spring Boot Anwendung wird der konkrete Einsatz illustriert.

Spring Boot bringt für Anwendungen automatische Konfigurationen mit. Damit können viele Features von Spring und Spring WebMVC direkt verwendet werden, ohne dass diese als Teil des Anwendungscodes aktiviert oder konfiguriert werden müssen.
Dies gilt auch für die Features zur Lokalisierung von Texten. Im Spring Framework wird dazu das MessageSource Interface bereitgestellt, das durch die ResourceBundleMessageSource implementiert wird. Dabei werden standard Java Resource Bundles verwendet, um im Properties Format Texte für verschiedene Locales bereitzustellen.

Spring Boot Internationalization

Spring Boot aktiviert eine automatische Konfiguration durch die MessageSourceAutoConfiguration. Ohne weitere Konfiguration wird dann nach messages*.properties Resource Bundles im Classpath gesucht.
Eine Anpassung ist durch das Spring Boot Property spring.messages.basename möglich, bei dem eine Liste von zu verwendenden Positionen im Classpath angegeben werden kann.
Beispiele für entsprechende Dateien sind im folgenden Listing zu sehen.

Beispiel für eine message.properties mit englischer Locale
welcome.message=Hello {0}, welcome!
error.message=Sorry, the request could not be processed.

Um zwischen den verschiedenen Locales zu unterscheiden wird das Kürzel der jeweiligen Locale als Suffix hinter dem Basisnamen der Datei verwendet.

Beispiel für eine message_de.properties mit deutscher Locale
welcome.message=Willkommen, {0}.
error.message=Das hat nicht geklappt, wir bitten um Entschuldigung.

Je nach verwendeter IDE werden diese Resource Bundles auch gesondert angezeigt. In IntellIJ sieht das beispielsweise wie im folgenden Screenshot aus.

Abbildung 1. Darstellung von Resource-Bundles in IntelliJ Idea IDE

Verwendung von Internationalization in Spring Boot

Spring Boot stellt eine Bean vom Typ MessageSource bereit, die dann ganz normal injected werden kann. Alternativ kann auch das Interface MessageSourceAware implementiert werden, dann wird die Methode setMessageSource(MessageSource messageSource) durch das Interface vorgegeben und entsprechend durch die Spring Dependency Injection aufgerufen.

Im folgenden Beispiel ist eine Klasse zu sehen, die sich einer MessageSource bedient, um lokalisierte Ausgaben zu ermitteln. Es handelt sich dabei lediglich um ein Verwendungsbeispiel, das keinen eigenen Mehrwert bereitstellt. Es spricht auch nichts dagegen, beispielsweise in der Controller-Schicht, direkt auf eine MessageSource zuzugreifen.

Beispiel zur Injection einer MessageSource
@Service
public class MessageProvider implements MessageSourceAware
{
    private MessageSource messageSource;

    @Override
    public void setMessageSource(MessageSource messageSource)
    {
        this.messageSource = messageSource;
    }

    public String resolve(String key, Locale locale, String ... args)
    {
        return messageSource.getMessage(key, args, locale);
    }
}

Um nun die zu nutzende Ausgabe zu ermitteln wird die MessageSource direkt oder indirekt aufgerufen. Dabei können auch Parameter mitgegeben werden, um innerhalb der Ausgabe variable Daten abzubilden. Eine Verwendung mit der oben gezeigten Beispielklasse in einem Controller könne wie folgt aussehen:

Verwendung lokalisierter Texte in einem Spring Controller
@GetMapping
public String welcome(String user, Locale locale)
{
    return messageProvider.resolve("welcome.message", locale, user);
}

Die Locale wird dabei automatisch von Spring WebMVC bereitgestellt und verwendet dazu die Default-Locale bzw. den Accept-Language HTTP-Header. Entsprechend sehen dann Beispielaufrufe der mit Spring Boot erstellten API aus.

Beispielaufrufe der Spring Boot Anwendung mit httpie
$ http :8080/api user==thomas
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8

Hello thomas, welcome!


$ http :8080/api user==thomas Accept-Language:de
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8

Willkommen, thomas.

Analog zu Controller kann dies Verfahren genauso auch bei der Fehlerbehandlung verwendet werden. Dazu werden typischerweise @ExceptionHandler in den jeweiligen Controller-Klassen oder querschnittlich in @RestControllerAdvice Klassen angegeben.

Beispiel zur Verwendung in einem @RestControllerAdvice
@RestControllerAdvice
public class ErrorHandlingAdvice
{
    private final MessageProvider messageProvider;

    public ErrorHandlingAdvice(MessageProvider messageProvider)
    {
        this.messageProvider = messageProvider;
    }

    @ExceptionHandler
    public String handleRuntime(RuntimeException ex, Locale locale)
    {
        return messageProvider.resolve("error.message", locale);
    }
}

Der zugehörige Aufruf und die lokalisierte Ausgabe ist im folgenden Beispiel zu sehen.

Beispielaufruf mit lokalisierter Fehlermeldung
$ http :8080/api/error Accept-Language:de
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8

Das hat nicht geklappt, wir bitten um Entschuldigung.

Speziell für die Entwicklung kann es sehr praktisch sein, wenn fehlende Angaben in den Messages-Dateien nicht zu einem Fehler führen, sondern stattdesssen der jeweillige Schlüssel ausgegeben wird.
Dies kann in Spring Boot durch das Property spring.messages.use-code-as-default-message gesteuert werden.




Zu den Themen Spring und Spring Boot bieten wir zudem sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Zur Desktop Version des Artikels