StartseitePortfolioBlog

i18n im Backend - Lösung für Internationalisierung auf mehreren Clients

By Mike Wegele
Published in Softwareentwicklung
August 16, 2023
3 min read
i18n im Backend - Lösung für Internationalisierung auf mehreren Clients

Dimacon, eines der Produkte von Miragon, ist eine ganzheitliche Plattform, mit der der Lebenszyklus eines Auftrags über alle Endgeräte digital abgebildet werden kann. Die verschiedenen Endgeräte umfassen sowohl die Web-App als auch eine Smartphone-App, über welche unsere Nutzer (Monteure, Büro) bestimmte Aufgaben durchführen können. Die Internationalisierung von Web- und Smartphone-Apps ist unerlässlich, um Benutzer auf der ganzen Welt zu erreichen. Dimacon hat diese Notwendigkeit erkannt und seine Web-App auf Englisch und Deutsch lokalisiert. Allerdings war die Smartphone-App bisher nur auf Deutsch verfügbar, was die Möglichkeit einer breiteren Nutzerbasis einschränkte. Um das Problem zu lösen, implementiert Dimacon eine effektive Internationalisierungslösung, die eine zentrale Verwaltung der Sprachressourcen im Backend nutzt. In diesem Blog-Post werden wir uns genauer ansehen, wie diese Lösung auf mehreren Clients umgesetzt wurde und welche Vorteile sie bietet.

Die Macht von i18n

Um Inhalte in mehreren Sprachen verfügbar zu machen, nutzt die Dimacon-Plattform das Framework i18n, das wohl bekannteste Internationalisierungsframework. Es handelt sich dabei um ein TypeScript-basiertes React-Frontend. Die i18n-Integration zeichnet sich dadurch aus, dass die Texte nicht direkt in die JSX-Elemente geschrieben werden. Stattdessen wird ein Verweis auf einen Wert angegeben, der durch den Schlüssel definiert ist. Dieser Schlüssel wird dann verwendet, um die entsprechende Übersetzung aus den JSON-Dateien abzurufen, die für jede Sprache vorhanden sind. Die Schlüssel-Wert-Paare liegen in mehreren JSON-Dateien im Backend vor.

Übersetzungen im Backend

Um die Übersetzungen für die Anzeige im Frontend zu laden, stellen wir i18n unsere Übersetzungsdateien im JSON-Format bereit. Dabei werden diese Dateien in einem speziellen Service geladen, welche in einem statischen Ordner gespeichert sind. Die Dateien sind nach Sprachcodes in Unterordnern organisiert, und alle Dateien haben denselben Namen common.json und den gleichen Aufbau der Schlüssel. Innerhalb jeder JSON-Datei sind die Texte für verschiedene Schlüssel in den jeweiligen Sprachen gespeichert.

Die Struktur des Ordners, in dem die JSON-Dateien gespeichert sind, ist so aufgebaut, dass es einen übergeordneten Ordner für alle Sprachcodes gibt, die von der Anwendung unterstützt werden. Innerhalb dieses Ordners gibt es dann einen separaten Unterordner für jede Sprache, der die entsprechenden common.json-Dateien enthält. Der Unterschied liegt in den Werten der Schlüssel, die durch die Sprache bestimmt werden.

Der Service lädt die JSON-Dateien aus dem Ressourcen-Ordner im Backend und kodiert sie in einen String im JSON-Format um. Um die JSON-Dateien für die jeweilige Sprache im Frontend anzuzeigen, muss der Client die entsprechende Datei über eine REST-Abfrage laden. Hierbei übergibt der Client den Sprach-Code, um die benötigte Sprache zu bestimmen.

Der Controller-Endpunkt definiert dann die entsprechende URI für die angeforderte Sprache. Anschließend wird die JSON-Datei durch das i18n-Framework geladen und die entsprechenden Texte im Frontend angezeigt, welches folgendermaßen im Controller geschrieben wurde.

@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@Tag(name = "Translations Controller")
@RequestMapping("/api/translations")
public class TranslationController {

    private final TranslationService translationService;

    @GetMapping("/{languageKey}")
    @Operation(summary = "Get common translation in specific language")
    public ResponseEntity<String> getSpecificTranslation(@PathVariable final String languageKey) {
        log.debug("Received request to get translation file for language with key '{}'", languageKey);
        return ResponseEntity.ok(this.translationService.getSpecificLanguageOld(languageKey));
    }
}

Wenn man eine GET-Anfrage mit /api/translations/DE sendet, erhält man die deutsche Übersetzungsdatei. Analog dazu erhält man mit der Abfrage /api/translations/EN die englische Datei. Der Service liest die JSON aus dem Ressources-Ordner.

public String getSpecificLanguage(final String languageKey) {
        final String pathToJson = String.format("translations/%s/common.json", languageKey);
        return this.loadTranslationsFromJson(pathToJson).orElse(null);
    }

private Optional<String> loadTranslationsFromJson(final String pathToJson) {
    try {
        final InputStream inputStream = new ClassPathResource(pathToJson).getInputStream();
        return Optional.of(IOUtils.toString(inputStream, StandardCharsets.UTF_8));
    } catch (final IOException e) {
        return Optional.empty();
    }
}

Konfiguration im Frontend

Nachdem beschrieben wurde, was die Grundlagen von i18n sind und wie die Übersetzungsdateien vom Backend bereitgestellt wurden, wird im letzten Teil erläutert, wie man die Dateien so lädt, dass i18n damit arbeiten kann. Außerdem wird erklärt, was passiert, wenn das Backend nicht ansprechbar ist und welche Meldung angezeigt wird, wenn die Anfrage keine Antwort erhält.

Eine Beispiel-Konfiguration könnte folgendermaßen aussehen.

const i18nConfig: InitOptions = {
    ns: "common",
    supportedLngs: ['DE', 'EN'],
    fallbackLng: 'DE',
    detection: {
        order: ['localStorage'],
        lookupLocalStorage: 'lng',
        checkWhitelist: true
    },
    backend: {
        backends: [
            HttpBackend,
            HttpBackend,
        ],
        backendOptions: [{
            loadPath: 'http://localhost:8084/api/translations/{{lng}}',
            requestOptions: {method: "GET"}
        }]
    },
}

Die einzelnen Attribute in der Konfiguration sind wie folgt zu interpretieren

NamespaceNamen der Datei, in der die Übersetzungen liegen
Unterstützte SprachenListe von Sprachcodes, die man i18n bereitstellt
Fallback-SpracheStandard-Sprache als Ersatz, wenn keine Übersetzung für die aktuelle Sprache verfügbar ist
Detectionnimmt Informationen entgegen, die zum Identifizieren und Speichern der ausgewählten Sprache des Nutzers verwendet werden
BackendObjekt, das aus einer Liste von Backends und Backend-Optionen besteht
Backend-ListeArt des Backend verwendet, um die Übersetzungen zu verwalten
Backend-OptionenBeschreibung, wie das Backend angefragt werden kann

Aus dieser Konfiguration ergibt sich die Frage: Was passiert mit i18n, wenn das Backend nicht vorhanden ist?

Fallback

Es scheint, dass eine Backend-Option fehlt, obwohl die Liste nur ein Element enthält und zwei Backends angegeben sind. Um dieses Problem zu lösen, müssen wir zunächst klären, wo das Fallback für die Übersetzungen liegt.

Es gibt zwei Ebenen für die verfügbaren Übersetzungen: Dateien aus dem Backend und lokale Übersetzungen als Fallback. Während der Entwicklung wird das Fallback regelmäßig aus dem Backend geladen und im Ordner public/locales gespeichert, damit der Client immer eine Ersatzdatei vorhalten kann. Dabei handelt es sich um dieselbe JSON-Datei wie im Backend, jedoch nur für die deutsche Sprache, da Deutsch als Fallback-Sprache angegeben wurde.

Um das Fallback zu laden, haben wir ein npm run Befehl im scripts-Tag der package.json hinzugefügt. Wenn dieser Befehl ausgeführt wird, wird die Datei mit dem Fallback von einem Backend-Endpunkt heruntergeladen und im Ordner public/locales/DE abgelegt, welches man im folgenden Befehl sehen kann.

"downloadFallback": "curl -X GET http://localhost:8084/api/translations/DE/common > public/locales/DE/common.json"

Nach der Ausführung des Befehls liegt die JSON-Datei im Ordner public/locales/DE.

Das zweite HttpBackend wird in zwei Fällen angesprochen.

  1. Wenn das erste Backend nicht erreichbar ist und daher keine Daten für i18n laden kann.
  2. Wenn keine Übersetzungsdatei für die aktuelle Sprache verfügbar ist.

Das zweite Backend sucht ohne Backend-Optionen im Ordner public nach der common.json, die als Fallback für die aktuelle Sprache verwendet wird. Da in diesem Beispiel Deutsch als Fallback-Sprache festgelegt wurde, wird die common.json aus dem Unterordner DE geladen.

Habt ihr eine ähnliche Aufgabenstellung bei euch im Projekt und konnte ich euch durch meinen Post weiterhelfen? Falls ihr Fragen oder Anmerkungen habt, könnt ihr euch gerne bei mir über LinkedIn oder per E-Mail melden.


Tags

#react#software-development#i18n
Previous Article
Miranum - Das neue Element für die Prozess-Automatisierung
Mike Wegele

Mike Wegele

Fullstack Entwickler

Ähnliche Beiträge

Miranum - Das neue Element für die Prozess-Automatisierung
May 30, 2023
6 min
© 2024, All Rights Reserved.

Links

StartseitePortfolioBPM TrainingÜber UnsKarriereBlog

Social Media