Flutter-Lokalisierung: der ultimative Leitfaden

Lass uns die Geheimnisse der Flutter-Lokalisierung entschlüsseln, damit du die Sprache deiner Nutzer sprichst und deinen Weg zur globalen Dominanz programmierst.

Flutter, Googles plattformübergreifendes App-Framework, hat nicht nur an Popularität im Bereich der mobilen App-Entwicklung gewonnen, sondern sich nahtlos auf das Web, Linux, macOS und Windows ausgeweitet. Darüber hinaus ist Flutter blitzschnell und es macht Spaß, damit zu arbeiten.

Wenn es um die Internationalisierung (i18n) von Flutter-Apps geht, hat das Flutter-Team eine solide integrierte Lösung entwickelt. In diesem Tutorial richtest du die i18n-Bibliotheken von Flutter ein und konfigurierst sie, lädst und zeigst Übersetzungen an und arbeitest dich durch die Formatierung von Datum und Uhrzeit sowie andere Lokalisierungsfunktionen.

🤿 Mehr Info » Flutters natives Lokalisierungspaket basiert auf dem hauseigenen Dart intl-Paket.

String Management UI visual | Phrase

Take your web or mobile app global without any hassle

Adapt your software, website, or video game for global audiences with the leanest and most realiable software localization platform.

Unsere Demo-App

Um die Dinge übersichtlich und unterhaltsam zu halten, werden wir eine kleine Demo-App erstellen und lokalisieren. Helden der Informatik präsentiert eine Auswahl bemerkenswerter Persönlichkeiten aus der relativ kurzen Geschichte der Informatik.

Startbildschirm Heroes of Computer Science | Phrase
Unsere App in Englisch – und bald in anderen Sprachen

Verwendete Versionen

Wir verwenden die folgenden Sprach-, Framework- und Paketversionen in diesem Artikel:

  • Dart 3.1.1
  • Flutter 3.13.3
  • DevTools 2.25.0
  • flutter_localizations (Version scheint an Flutter gebunden zu sein) – bietet Lokalisierungen für gängige Widgets wie Material- oder Cupertino-Widgets.
  • intl 0.18.0 – das Rückgrat des Lokalisierungssystems; es ermöglicht uns, unsere eigenen Lokalisierungen zu erstellen und zu verwenden; wird zur Formatierung von Datumsangaben und Zahlen verwendet.

Schauen wir uns den Code für unsere Starter-App an, der ziemlich einfach ist.


import 'package:flutter/material.dart';
import 'screens/hero_list.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Heroes of Computer Science',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HeroList(title: 'Heroes of Computer Science'),
    );
  }
}Code-Sprache: Dart (dart)

Unser Root-Widget ist eine grundlegende MaterialApp, mit einer HeroList an ihrer home-Route.

import 'package:flutter/material.dart';
import 'package:flutter_i18n_2021/screens/settings.dart';
import 'package:flutter_i18n_2021/widgets/hero_card.dart';

class HeroList extends StatelessWidget {
  final String title;

  HeroList({this.title = ''});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.settings),
            tooltip: 'Open settings',
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => Settings()),
              );
            },
          )
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Text('6 Heroes'),
            ),
            Expanded(
              child: ListView(
                children: <Widget>[
                  HeroCard(
                    name: 'Grace Hopper',
                    born: '9 December 1906',
                    bio: 'Devised theory of machine...',
                  ),
                  HeroCard(
                    name: 'Alan Turing',
                    born: 23. Juni 1912
                    bio: 'Father of theoretical computer...',
                  ),
                  // ...
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}Code-Sprache: Dart (dart)

HeroList beherbergt hauptsächlich eine ListView mit parametrisierten HeroCard-Einträgen.

import 'package:flutter/material.dart';

class HeroCard extends StatelessWidget {
  final String name;
  final String born;
  final String bio;
  final String imagePath;
  final String placeholderImagePath = 'assets/images/placeholder.jpg';

  const HeroCard({
    Key key,
    this.name = '',
    this.born = '',
    this.bio = '',
    this.imagePath,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    var theme = Theme.of(context);

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(4.0),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(right: 8.0),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(2),
                child: Image.asset(
                  imagePath ?? placeholderImagePath,
                  width: 100
                  height: 100, 
                ),
              ),
            ),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.only(top: 4),
                    child: Text(
                      name,
                      style: theme.textTheme.headline6,
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.only(top: 2, bottom: 4),
                    child: Text(
                      born.isEmpty ? '' : 'Born $born',
                      style: TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.w300,
                      ),
                    ),
                  ),
                  Text(
                    bio,
                    style: TextStyle(fontSize: 14),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Code-Sprache: JavaScript (javascript)

HeroCard zeigt das angegebene Bild und die Textparameter in einem schicken Material-Card-Widget und ordnet alles attraktiv an. Gut, dann sollten wir mit der Lokalisierung loslegen!

🔗 Ressource » Du kannst den Code der App bis zu diesem Punkt aus dem start-Branch unseres GitHub-Repository erhalten. Der main branch enthält außerdem die vollständig lokalisierte App.

Installation und Einrichtung

Wir können unsere Pakete installieren, indem wir ein paar Zeilen zu pubspec.yaml hinzufügen.

Version: 1.0.0+1

environment:
  sdk: '>=3.1.1 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
    
  cupertino_icons: ^1.0.2

  # Füge das flutter_localizations-Paket hinzu
  flutter_localizations:
    sdk: flutter
  # Füge das intl-Paket hinzu
  intl: ^0.18.0

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  generate: true # Füg diese Zeile hinzu
  uses-material-design: trueCode-Sprache: YAML (yaml)

Nachdem du die oben hervorgehobenen Zeilen hinzugefügt hast, kannst du flutter pub get im Terminal ausführen, um unsere Pakete zu installieren. Die Zeile generate: true ist notwendig für die automatische Codegenerierung, die die Lokalisierungspakete für uns bereitstellen. Wir schauen uns gleich genauer an, wie Code generiert wird. Füg fürs Erste diese Zeile hinzu. Sie kann dir eine Menge Zeit sparen.

Lokalisierung konfigurieren

Wenn die Pakete installiert sind, füge eine l10n.yaml-Datei zum Stammverzeichnis deines Projekts hinzu. Diese Datei konfiguriert, wo sich unsere Übersetzungsdateien befinden und welche Namen die automatisch generierten Dart-Dateien haben.

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dartCode-Sprache: YAML (yaml)

🔗 Quelle » Der offizielle Internationalization User Guide zeigt dir viele weitere Optionen, die du in l10n.yaml eintragen kannst, um den Flutter i18n-Codegenerator zu steuern.

Übersetzungsdateien hinzufügen

Die Flutter-Lokalisierung verwendet standardmäßig ARB (Application Resource Bundle)-Dateien, um die Übersetzungen zu verwalten. Dies sind einfache Dateien, die in JSON-Syntax geschrieben sind. Zumindest benötigen wir eine Vorlagendatei, die unserer Standardsprache (in unserem Fall Englisch) entspricht. Wir haben in unserer obigen Konfiguration angegeben, dass unsere Vorlagendatei lib/l10n/app_en.arb ist. Lass uns dieses Übersetzungsverzeichnis erstellen und unsere Übersetzungsvorlagendatei hinzufügen.

{
  "appTitle": "Heroes of Computer Science"
}Code-Sprache: JSON / JSON mit Kommentaren (json)

Natürlich würden all diese Spielereien nicht viel Sinn ergeben, wenn wir keine Übersetzungen für andere Sprachen bereitstellen könnten. Hier werden wir eine arabische Übersetzungsdatei hinzufügen. Du kannst jede Sprache hinzufügen, die du möchtest. Wir gehen später noch kurz auf Layouts von rechts nach links (RTL) ein. Wenn du daran interessiert bist, bleib am besten bei Arabisch oder einer anderen RTL-Sprache.

{
  "appTitle": "أبطال علوم الكمبيوتر"
}Code-Sprache: JSON / JSON mit Kommentaren (json)

Wir können so viele Sprachversionen hinzufügen, wie wir möchten. Wir müssen nur darauf achten, dass unsere Dateien der konfigurierten Namenskonvention entsprechen: lib/l10n/app_<locale>.arb

Konfigurieren unserer App

Lass uns unserer App von unserem großen Interesse an i18n erzählen. Du musst deine Datei main.dart so konfigurieren, dass die Flutter-Lokalisierungspakete verwendet werden.

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'screens/hero_list.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Heroes of Computer Science',
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        // 'en' ist der Sprachcode. Wir könnten optional einen
        // Ländercode als zweiten Parameter angeben, z. B.
        // Locale('en', 'US'). Wenn wir das tun, sollten wir vielleicht
        // eine zusätzliche app_en_US.arb-Datei für
        // regionsspezifische Übersetzungen bereitstellen.
        const Locale('en', ''),
        const Locale('ar', ''),
      ],
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HeroList(title: 'Heroes of Computer Science'),
    );
  }
}
Code-Sprache: JavaScript (javascript)

Nachdem du flutter_localizations.dart importiert hast, fügst du die localizationsDelegates– und supportedLocales-Eigenschaften zum MaterialApp-Konstruktor hinzu. localizationsDelegates stellen die Lokalisierungen für deine App bereit. Die oben aufgeführten bieten Lokalisierungen für Flutter Widgets, Material und Cupertino, die bereits vom Flutter-Team lokalisiert wurden.

Zum Beispiel, stell dir vor, wir hätten eine MaterialApp und würden irgendwo darin die Funktion showDatePicker() aufrufen. Angenommen, dein Betriebssystem ist auf Arabisch eingestellt, würdest du etwa Folgendes sehen.

Flutter showDatePicker() Kalender | Phrase

Beachte, dass wir nichts selbst übersetzen mussten, um das zu bekommen. Das Datumsauswahl-Widget wurde bereits vom Flutter-Team lokalisiert. Wir müssen nur die richtigen Delegates in unseren App-Konstruktor einbinden, wie wir es oben gemacht haben. Ein großes Lob an das Flutter-Team dafür: Was für eine Zeitersparnis!

🗒️ Hinweis » Zum Zeitpunkt des Schreibens unterstützt flutter_localizations 78 Sprachen.

🔗 Quelle » Die offizielle Flutter-Dokumentation erklärt gut, wie die verschiedenen Teile, wie Delegates und die Localizations-Klasse, zusammenarbeiten, um i18n/l10n zu ermöglichen.

Die supportedLocales-Eigenschaft, die wir dem MaterialApp-Konstruktor übergeben haben, listet die Sprachen auf, die unsere App unterstützt. Flutter wird nur Widgets als Reaktion auf eine Änderung der Spracheinstellung neu erstellen, wenn sich die Spracheinstellung in der Liste supportedLocales befindet. Wir kommen gleich auf supportedLocales zurück, wenn wir die Sprachauswahl besprechen. Lass uns jetzt etwas Code generieren!

Automatische Codegenerierung

Um die Übersetzungen aus den ARB-Dateien in unserer Flutter-App zu verwenden, müssen wir einige Dart-Dateien generieren, die wir importieren, wenn wir Übersetzungen brauchen. Um diese Dateien zu generieren, stelle einfach sicher, dass du die Installations- und Einrichtungsschritte bis zu diesem Punkt befolgt hast und die App ausführst. Du hast richtig gelesen: Führ einfach die App aus. Der Code wird automatisch generiert, und wenn alles gut gelaufen ist, solltest du die folgenden Dateien in deinem Projektverzeichnis sehen:

  • .dart_tool/flutter_gen/gen_l10n/app_localizations.dart
  • .dart_tool/flutter_gen/gen_l10n/app_localizations_en.dart
  • .dart_tool/flutter_gen/gen_l10n/app_localizations_ar.dart

🗒️ Hinweis » Wenn diese Dateien nicht generiert wurden, stelle sicher, dass deine Flutter-App keine Kompilierungsfehler hat und überprüfe deine Debug-Konsole, wenn du die App ausführst.

So nutzt du unsere AppLocalizations

Lass uns die neu generierten Code-Dateien nutzen, um unseren App-Titel zu lokalisieren.

import 'package:flutter/material.dart';
import 'package:flutter_i18n_2021/screens/settings.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'screens/hero_list.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      onGenerateTitle: (context) {
        return AppLocalizations.of(context).appTitle;
      },
      localizationsDelegates: [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en', ''),
        const Locale('ar', ''),
      ],
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // Startseite entfernen: HeroList(...)
      initialRoute: '/',
      routes: {
        '/': (context) => HeroList(title: AppLocalizations.of(context).appTitle),
        '/settings': (context) => Settings(),
      },
    );
  }
}
Code-Sprache: Dart (dart)

Importiere app_localizations.dart und füge den automatisch generierten AppLocalizations.delegate zu deiner Liste der Delegates hinzu. Damit bekommst du das AppLocalizations-Widget, mit dem du den App-Titel und den HeroList-Titel übersetzen kannst. Die automatisch generierte Eigenschaft appTitle enthält die Übersetzung, die der aktiven Sprache entspricht und aus unserer Datei app_<locale>.arb entnommen wird.

Hinweis » Aufgrund der Ladereihenfolge sind unsere Übersetzungen nicht bereit, wenn wir unsere MaterialApp erstellen. Deshalb benutzen wir die onGenerateTitle– und routes-Props sowie deren Builder-Funktionen (context) {}, damit die Übersetzungen bereit sind, wenn man die Titel festlegt.

Dann stellen wir die Sprache unseres Betriebssystems auf Arabisch ein, führen unsere App aus, und siehe da …

Helden der Informatik mit arabischem Titel | Phrase

Unser Titel wird jetzt auf Arabisch angezeigt. Außerdem sehen wir, dass Flutter viele seiner Widgets automatisch von rechts nach links für uns angeordnet hat. Da Arabisch eine von rechts nach links geschriebene Sprache ist, spart uns das eine Menge Zeit! Wir müssen das Padding links vom Bild in den HeroCard-Einstellungen korrigieren, und das machen wir, wenn wir uns später um die Ausrichtung kümmern.

🤿 Mehr Info » Scharfsichtige Leser*innen haben warscheinlich bemerkt, dass AppLocalizations.of(context) sich sehr ähnlich anfühlt wie das Aufrufen eines InheritedWidget. Das liegt daran, dass Lokalisierungsobjekte ähnlich wie InheritedWidget funktionieren.

Das war’s mit dem Setup. Wir haben jetzt die Grundlage für die Lokalisierung unserer App. Eine Frage, die du an diesem Punkt haben könntest, ist: „Wie entscheidet Flutter, welche Sprache verwendet wird?“ Lass uns darüber sprechen.

Spracheinstellung

Die Locales, die wir MaterialApp(supportedLocales: [...]) bereitgestellt haben, sind die einzigen, die Flutter verwendet, um die aktive Locale zu bestimmen, wenn die App ausgeführt wird. Um dies zu tun, verwendet Flutter drei Eigenschaften eines Gebietsschemas:

  • Den Sprachcode, z. B. 'en' für Englisch
  • Den Ländercode (optional), z. B. den US Teil in en_US
  • Den Skriptcode (optional) – der verwendete Schriftsatz, z. B. traditionelles (Hant) oder vereinfachtes Chinesisch (Hans)

Standardmäßig liest Flutter die bevorzugten Systemsprachen und -regionen deines Geräts aus und sagt sich:

  1. Versuche, den languageCode, scriptCode und countryCode mit einem Code in supportedLocales abzugleichen. Wenn das fehlschlägt:
  2. Versuche, den languageCode und scriptCode mit einem Code in supportedLocales abzugleichen. Wenn das fehlschlägt:
  3. Versuche, den languageCode und countryCode mit einem Code in supportedLocales abzugleichen. Wenn das fehlschlägt:
  4. Versuche, den languageCode mit einem Code in supportedLocales abzugleichen. Wenn das fehlschlägt:
  5. Versuche, den countryCode nur dann mit einem in supportedLocales abzugleichen, wenn alle bevorzugten Gebietsschemata nicht übereinstimmen. Wenn das fehlschlägt:
  6. Gib das erste Element von supportedLocales als Standardwert zurück.

In unserer App siehst du unsere ar Lokalisierungen (siehe 4.), wenn deine iOS-Sprache auf ar_SA eingestellt ist. Wenn deine iOS-Sprache auf fr (Französisch) eingestellt ist, siehst du unsere en Übersetzungen (siehe 6. oben). In Android kannst du eine Liste bevorzugter Gebietsschemata haben, nicht nur ein einzelnes. Dies wird von Flutter im obigen Auflösungs-Algorithmus behandelt.

🔗 Quelle » Der obige Algorithmus ist eine Umschreibung der offiziellen Dokumentation der supportedLocales property.

Hinweis » Wenn deine App eine Sprache mit einem Ländercode unterstützt, wie fr_CA (Kanadisches Französisch), solltest du zusätzlich eine Version ohne Ländercode bereitstellen, wie fr.

Aktualisierung des iOS-Projekts

Die offizielle Flutter-Dokumentation erwähnt die Notwendigkeit, die Info.plist direkt im iOS App-Bundle zu aktualisieren und unsere unterstützten Gebietsschemata hinzuzufügen. Wenn Info.plist nicht aktualisiert wird, funktioniert unsere iOS-App möglicherweise nicht wie erwartet. Um das Update durchzuführen, öffne einfach ios/Runner/Info.plist in einem beliebigen Texteditor und sorge dafür, dass die folgenden Einträge vorhanden sind.

<key>CFBundleLocalizations</key>
<array>
  <string>en</string>
  <string>ar</string>
</array>Code-Sprache: Klartext (plaintext)

Abrufen der aktiven Sprache

Manchmal müssen wir wissen, welche runtime locale in unserem Code verwendet wird. Das geht mit folgendem Snippet.

Locale activeLocale = Localizations.localeOf(context);

// angenommen, unsere aktive Sprache ist fr_CA ...
debugPrint(activeLocale.languageCode);
// => fr

debugPrint(activeLocale.countryCode);
// => CACode-Sprache: Dart (dart)

Hinweis » Beachte, dass wir Localizations verwenden, ein in Flutter integriertes Widget, und nicht die automatisch generierten AppLocalizations.

Grundlegende Übersetzungsnachrichten

Wir haben uns schon mit den grundlegenden Übersetzungsnachrichten beschäftigt, als wir unsere appTitle-Nachricht hinzugefügt haben. Lass uns aber kurz den Workflow zum Hinzufügen von Nachrichten durchgehen. Als Nächstes übersetzen wir das Tooltip des App-Bar-Icon-Buttons von HeroList. Wir können ja etwas davon nutzen, wo wir schon nicht einmal einen Einstellungsbildschirm bauen 😅.

// ...

class HeroList extends StatelessWidget {
  final String title;

  HeroList({this.title = ''});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        actions: <Widget>[
          IconButton(
            Symbol: Icon(Icons.settings),
            tooltip: 'Open settings',
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => Settings()),
              );
            },
          )
        ],
      ),
      body: ...
  );
}

Code-Sprache: Dart (dart)

Lass uns den Tooltip lokalisieren, okay? Zuerst fügen wir die relevanten Einträge zu unseren ARB-Dateien hinzu.

//Englisch
{
  "appTitle": "Heroes of Computer Science",
  "openSettings": "Open Settings"
}

// Arabisch
{
  "appTitle": "أبطال علوم الكمبيوتر",
  "openSettings": "إفتح الإعدادات"
}Code-Sprache: JSON / JSON mit Kommentaren (json)

Als nächstes lass uns unsere App neu laden, um unsere Code-Dateien neu zu generieren. Dieser Schritt ist wirklich wichtig und sollte nicht vergessen werden, wenn man Frustrationen vermeiden will. Beachte, dass dies ein vollständiger Neustart der App ist (

Aktualisieren | Phrase

), kein Hot Reload.

Jetzt können wir unseren Code aktualisieren, um unsere neue lokalisierte Nachricht zu verwenden.

// ... 
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class HeroList extends StatelessWidget {
  final String title;

  HeroList({this.title = ''});

  @override
  Widget build(BuildContext context) {
    var t = AppLocalizations.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        actions: <Widget>[
          IconButton(
            Symbol: Icon(Icons.settings),
            Tooltip: t.openSettings,
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => Settings()),
              );
            },
          )
        ],
      ),
      body: ... 
    );
  }
}
Code-Sprache: Dart (dart)

Mit diesem Code siehst du beim Neuladen deiner App den lokalisierten Wert deines Tooltips im Widget Inspector.

Widget Inspector Tooltip | Phrase

Hinweis » Du bekommst möglicherweise häufig Fehlerhervorhebungen in deiner IDE, nachdem du neue Übersetzungsnachrichten hinzugefügt hast. Wenn du deine App neu geladen hast, könnte der Fehler falsch sein (vielleicht ist alles in Ordnung). Wenn du eine Nachricht bekommst, dass es Build-Fehler gibt, kannst du trotzdem versuchen, die App zu starten. Solange die App gebaut und ausgeführt wird und du deine neuen Übersetzungen siehst, ist wahrscheinlich alles in Ordnung. Um den Fehler in der IDE zu beheben, fahr deine App komplett herunter und starte sie dann neu.

Interpolation in Nachrichten

Wir sind auf dem Weg, unsere App zu übersetzen. Aber was ist mit dem Interpolieren von dynamischen Laufzeitwerten in unseren Übersetzungen? Zum Beispiel enthält Steve Wozniaks Biografie die Produktnamen Apple I und Apple II.

// ... 

class HeroList extends StatelessWidget {
  // ... 

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ... 
      body: HeroCard(
        name: 'Steve Wozniak',
        born: 11. August 1950,
        bio: 'Designed & developed the Apple I & '
             'Apple II microcomputers.',
        imagePath: 'assets/images/steve_wozniak.jpg',
      ),
      // ... 
    );
  }
}Code-Sprache: Dart (dart)

Wenn wir diese Nachricht lokalisieren, sollten wir wahrscheinllich Apple I und Apple II in ihrem ursprünglichen Englisch beibehalten, unabhängig von der aktiven Locale. Wir können Platzhalter in unseren Übersetzungsdateien verwenden, um dies zu erreichen.

//Englisch
{
  // ...
  "wozniakBio": "Developed the {appleOne} & {appleTwo} microcomputers.",
  "@wozniakBio": {
    "placeholders": {
      "appleOne": {},
      "appleTwo": {}
    }
  },
  
  // ...
}

// Arabisch
{
  // ...
  "wozniakBio": "طور جهازي كمبيوتر {appleOne} و {appleTwo}",
  // ...
}
Code-Sprache: JSON / JSON mit Kommentaren (json)

Wir verwenden die Syntax {placeholderName}, um die Platzhalter für unsere dynamischen Werte festzulegen und wir können so viele Platzhalter haben, wie wir in einer Nachricht möchten.

Du hast wahrscheinlich auch den Key @wozniakBioin unseren oben genannten englischen Übersetzungen bemerkt. Dieser Eintrag ist ein Begleittext zur Nachricht wozniakBio in derselben Datei. Begleittexte sind für grundlegende Nachrichten optional, aber für Nachrichten mit Platzhaltern erforderlich. Tatsächlich verwenden wir Begleiteinträge, um Nachrichtenplatzhalter zu definieren (unter anderem).

🗒️ Hinweis » Der Begleiteintrag für eine Nachricht mit dem Key foo muss einen Key namens @foo haben. Wir benötigen nur Begleiteinträge in unserer Standard-/Vorlagen-Übersetzungsdatei (in unserem Fall Englisch).

"@wozniakBio": { "placeholders": { "appleOne": {}, "appleTwo": {} } }Code-Sprache: JavaScript (javascript)

Hinweis » Platzhalternamen müssen gültige Dart-Methodenparameternamen sein.

Wir könnten das Objekt placeholders Objekt verwenden, um den Typ jedes Wertes anzugeben und nach Wunsch sogar Beispiele als Dokumentation bereitstellen. Wir können die Definition aber auch als leeres {} belassen.

"@wozniakBio": {
    "placeholders": {
      "appleOne": {
        // Expliziter Typ
        "type": "String",
        // Eine kleine Doku
        "example": "Apple I"
      },
      // Es ist vollkommen in Ordnung, nur den Namen anzugeben
      "appleTwo": {}
    }
  }Code-Sprache: JavaScript (javascript)

Der type wird in der Methode verwendet, die Flutter auf AppLocalizations für unsere wozniakBio Nachricht generiert.

// ... 

abstract class AppLocalizations {
  // ... 

  // Diese Methode ist in app_localizations_en.dart und
  // app_localizations_ar.dart implementiert.
  // Expliziter Typ für den appleOne-Parameter. Implicit appleTwo
  // Parameter.
  String wozniakBio(String appleOne, Object appleTwo);

  // ... 
}
Code-Sprache: JavaScript (javascript)

🗒️ Hinweis » Es ist in den meisten Fällen völlig okay, leere {} für Platzhalterdefinitionen zu verwenden. Eine leere Definition führt dazu, dass der Parameter vom Typ Object sein wird. Im Hintergrund verwendet Flutter einfach den Wert theParameter.toString() des gegebenen Parameters, sodass ein Object-Parameter gut funktioniert. Ein impliziter Object Platzhalter hält unsere Nachrichten flexibel, sodass sie jeden Typ akzeptieren können, da alle Dart-Typen von Object abgeleitet sind und über toString() verfügen.

Okay: Nachdem du die App neu gestartet hast, um AppLocalizations zu aktualisieren, kannst du die Bio von Woz mit der neuen Methode lokalisieren.

// ... 

class HeroList extends StatelessWidget {
  // ... 

  @override
  Widget build(BuildContext context) {
    var t = AppLocalizations.of(context);

    return Scaffold(
      // ... 
      body: HeroCard(
        name: 'Steve Wozniak',
        born: '11 August 1950',
        bio: t.wozniakBio('Apple I', 'Apple II'),
        imagePath: 'assets/images/steve_wozniak.jpg',
      ),
      // ... 
    );
  }
}
Code-Sprache: Dart (dart)

Damit wissen wir, dass wir niemals von fruchtaromatisierten Unternehmen verklagt werden können, weil wir ihre Produkte in irgendeiner Sprache falsch darstellen.

Interpolierte Werte in Arabisch und Englisch | Phrase
Interpolierte Werte in Arabisch und Englisch

Pluralformen

Häufig müssen wir mit dynamischen Pluralformen in unserer Lokalisierung umgehen. Ein Beispiel wäre: „Du hast eine Nachricht erhalten“ und „Du hast 3 Nachrichten erhalten“.

Es ist wichtig zu beachten, dass verschiedene Sprachen Pluralformen unterschiedlich behandeln. Zum Beispiel hat Englisch zwei Pluralformen: one und other (other == null und >1). Arabisch hat sechs Pluralformen. Das kann ein bisschen Kopfschmerzen bereiten, wenn du Bibliotheken verwendest, die keine komplexen Pluralregeln unterstützen. Glücklicherweise behandelt die i18n-Lösung von Flutter komplexe Pluralformen direkt. Damit bist du auf der sicheren Seite. Lass uns damit den Heldenzähler in unserer App lokalisieren.

Helden der Informatik Startbildschirm, englische Pluralisierung | Phrase
// ...

class HeroList extends StatelessWidget {
  // ...

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ...
      body: Padding(
        // ...
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              // Lokalisierte Zeichenfolge soll '6 Heroes' ersetzen
              child: Text('6 Heroes'), 
            ),
            // ...
          ],
        ),
      ),
    );
  }
}

// ...
Code-Sprache: Dart (dart)

Zuerst fügen wir die Nachricht unserer englischen ARB-Vorlagendatei hinzu.

{
  // ...
  "heroCount": "{count,plural, =0{No heroes yet} =1{1 hero} other{{count} heroes}}",
  "@heroCount": {
    "placeholders": {
      "count": {}
    }
  },
  // ...
}
Code-Sprache: JSON / JSON mit Kommentaren (json)

Dann geben wir den Platzhalter count in der Nachricht ein und verwenden ihn mit der speziellen {count,plural,...} Syntax, um die verschiedenen Pluralformen zu definieren.

Hinweis » Der count-Parameter ist immer vom Typ int. Wenn du einen anderen Typ für count angibst, ignoriert Flutter ihn und verwendet trotzdem int.

🗒️ Hinweis » Du kannst neben count auch andere Platzhalter zu einer Pluralnachricht hinzufügen; sie werden wie gewohnt angegeben (siehe Interpolation oben).

Flutter unterstützt die folgenden Pluralformen.

  • zero ➞ =0{No heroes}
  • one ➞ =1{One hero}
  • two ➞ =2(Two heroes}
  • few ➞ few{The {count} heroes}
  • many ➞ many{{count} heroes}
  • other ➞ other{{count} heroes}

few, many, and other haben unterschiedliche Bedeutungen, abhängig von der aktiven Sprache. Die einzige erforderliche Form in jeder Sprache ist die Form other.

🗒️ Hinweis » Wir mussten die zero-=0-Form in unserer englischen Nachricht oben nicht verwenden. Hätten wir sie weggelassen, hätte Flutter stattdessen unsere Form other verwendet.

Alles klar, lass uns unsere arabische Nachricht hinzufügen. Wie wir bereits erwähnt haben, hat Arabisch sechs Pluralformen.

{
  // ...
  "heroCount": "{count,plural, =0{لا توجد أبطال بعد} =1{بطل واحد} =2{بطلان} few{{count} أبطال} many{{count} بطل} other{{count} بطل}}",
  // ...
}Code-Sprache: JSON / JSON mit Kommentaren (json)

Jetzt lass uns alles verbinden und unsere neue Nachricht in unserem HeroList-Widget verwenden.

// ...

class HeroList extends StatelessWidget {
  // ...

  @override
  Widget build(BuildContext context) {
    var t = AppLocalizations.of(context);
    return Scaffold(
      // ...
      body: Padding(
        // ...
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Text(t.heroCount(6)),
            ),
            // ...
          ],
        ),
      ),
    );
  }
}

// ...
Code-Sprache: Dart (dart)

Natürlich wäre der count-Parameter, der an t.heroCount() übergeben wird, in einer Produktivanwendung dynamisch. Flutter wählt je nach aktivem Gebietsschema die korrekte Pluralform aus unserer Nachricht aus.

Englische Pluralformen in unserer App | Phrase
Englische Pluralformen in unserer App
Arabische Pluralformen in unserer App | Phrase
Arabische Pluralformen in unserer App

Zahlenformatierung

Du kannst Zahlen in deinen lokalisierten Nachrichten formatieren, indem du das placeholders-Objekt in den Begleit-Einträgen deiner ARB-Vorlagendatei (in unserem Fall Englisch) verwendest. Es gibt keinen guten Platz, um die Zahlenformatierung in unserer kleinen Demo-App unterzubringen, also tun wir einfach so, als hätten wir eine E-Commerce-App, um das zu demonstrieren.

// app_en.arb in der Beispiel-App, die einen Warenkorb hat 
{
  "itemTotal": "Your total is: {value}",
  "@itemTotal": {
    "placeholders": {
      "value": {
        "type": "double",
        "format": "currency"
      }
    }
  }
}
Code-Sprache: JSON / JSON mit Kommentaren (json)

Beachte, dass wir einen expliziten type und format angegeben haben, um zu steuern, wie die Zahl angezeigt wird. Wie gewohnt können wir unsere Nachricht in unseren anderen Sprachdateien übersetzen.

// app_ar.arb in der Beispiel-App, die einen Warenkorb hat
{ "itemTotal": "إجمالي: {value}" }Code-Sprache: JSON / JSON mit Kommentaren (json)

Nachdem wir unsere App neu geladen haben, können wir unsere Nachricht wie gewohnt verwenden.

// In einem Widget
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

// In einem Widget-Builder mit einem Kontext
var t = AppLocalizations.of(context);
var message = t.itemTotal(56.12);
// => "Your total is USD56.12", wenn die aktuelle Sprache Englisch ist
// => "إجمالي: EGP56,12, wenn die aktuelle Sprache Arabisch ist
Code-Sprache: Dart (dart)

Hinweis » Du kannst die Zahlen formate pro Sprache nicht überschreiben. Das Format, das du in deiner Vorlagen-Sprache (Englisch in unserem Fall) angibst, wird in allen Sprachen verwendet, unabhängig von einer format-Überschreibung, die du in deinen anderen Sprachdateien angibst.

Denk daran, dass Flutter im Hintergrund die Dart intl– Bibliothek für die meisten seiner Internationalisierungsaufgaben verwendet. Das Währungsformat, das wir oben verwendet haben, ist eines von mehreren Formaten, die im Intl NumberFormat vordefiniert sind. Andere Formate umfassen Dezimalzahlen, Prozentsätze und mehr.

🔗 Quelle » Schau dir das offizielle Benutzerhandbuch für alle verfügbaren Formate an.

Wir müssen uns aber nicht auf Flutter verlassen, um unsere Zahlen an intl zu übergeben. Wir können intl direkt verwenden, um mehr Kontrolle über unsere Zahlenformatierung zu haben.

import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:intl/intl.dart';

// In einem Widget-Builder mit einem Kontext
var currentLocale = AppLocalizations.of(context).localeName;
var compact = NumberFormat.compact(locale: currentLocale).format(6000000);
// => "6M": wenn die aktuelle Sprache US-Englisch ist
// => "٦ مليون", wenn das aktuelle Gebietsschema ägyptisches Arabisch ist

var simpleCurrency = NumberFormat.simpleCurrency(locale: currentLocale).format(14.24);
// => "$14.24", wenn die aktuelle Sprache US-Englisch ist
// => "ج.م.‏ ١٤٫٢٤" wenn die aktuelle Sprache auf Ägyptisch-Arabisch eingestellt istCode-Sprache: Dart (dart)

🔗 Ressource » Du musst die vordefinierten Formate wie compact und simpleCurrency nicht verwenden. Der intl NumberFormat-Konstruktor gibt dir detaillierte Kontrolle über deine Zahlenformate. Alle Informationen findest du in der offiziellen Dokumentation.

Hinweis » Der einzige Weg, wie ich östliche arabische Ziffern (١،٢،٣…) für Arabisch anzeigen lassen konnte, bestand darin, den locale-Parameter auf "ar_EG" (Ägyptisches Arabisch) zu setzen. Weder "ar" noch irgendeine "ar_XX" variante, außer der ägyptischen Variante, hat bei mir funktioniert.

Hinweis » Formate haben für mich mit der Pluralvariable count nicht funktioniert. Es scheint, dass Flutter das Format überschreibt, wenn es Pluralformen verarbeitet. Wenn du in deinen Pluralformen Formate bekommen kannst, lass uns unten in den Kommentaren wissen, wie du das gemacht hast.

Datumsformatierung

Unsere Helden haben derzeit hartcodierte Geburtsdaten, die nicht lokalisiert sind – was ziemlich uncool ist.

Englisches Datumsformat in arabischer Lokalisierung | Phrase
Wir wollen, dass dieses Datum auf Arabisch lokalisiert wird.

Denk daran, dass wir jeden unserer Helden mit einem HeroCard-Widget rendern.

import 'package:flutter/material.dart';

class HeroCard extends StatelessWidget {
  final String name;
  final String born;
  final String bio;
  final String imagePath;
  // ...

  const HeroCard({
    Key key,
    this.name = '',
    this.born = '',
    this.bio = '',
    this.imagePath,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // ...
    return Card(
      child: Padding(
        // ...
        Padding(
          padding: const EdgeInsets.only(top: 2, bottom: 4),
          child: Text(
            born.isEmpty ? '' : 'Born $born',
            // ...
          ),
        ),
        // ...
      ),
    );
  }
}Code-Sprache: Dart (dart)

Um das born-Datum für jedes Gebietsschema, das unsere App unterstützt, zu formatieren, füge zuerst ein paar neue lokalisierte Nachrichten mit interpolierten Datumswerten hinzu.

//Englisch
{
  // ...
  "heroBorn": "Born {date}",
  "@heroBorn": {
    "placeholders": {
      "date": {
        "type": "DateTime",
        "format": "yMMMd"
      }
    }
  },
  // ...
}

// Arabisch
{
  // ...
  "heroBorn": "تاريخ الميلاد {date}",
  // ...
}Code-Sprache: JSON / JSON mit Kommentaren (json)

Wenn wir unseren date-Platzhalter in unserer Vorlagendatei für die Lokalisierung definieren, müssen wir ihm den Typ DateTime geben. Wir können dann ein format verwenden, um anzugeben, wie wir das Datum anzeigen möchten. Das Format yMMMd, das wir oben definiert haben, steht für „Jahr, abgekürzter Monat, Tag“, was im US-Englisch etwa „Dec 9, 1906“ ergeben würde.

🔗 Quelle » Tatsächlich verwendet Flutter im Hintergrund einfach die DateFormat-Klasse von intl, die Code wie DateFormat.yMMMd(localeName).format(date) generiert. Der yMMMd benannte Konstruktor ist eine praktische Abkürzung, die als „Skelett“ bezeichnet wird, und es gibt eine ganze Reihe dieser Skelette, die wir verwenden können. Schau sie dir in der offiziellen DateFormat-Dokumentation an.

🗒️ Hinweis » Wir mussten die Platzhaltervariable nicht date nennen. Wir hätten ihr jeden Namen geben können, solange es ein gültiger Dart-Funktionsparameter ist.

Alles klar: Lass uns das in unserem Widget einbauen, damit unsere neuen Nachrichten angezeigt werden.

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class HeroCard extends StatelessWidget {
  final String name;
  final String born;
  final String bio;
  final String imagePath;
  // ...
  final DateTime bornDateTime;

  HeroCard({
    Key key,
    this.name = '',
    this.born = '',
    this.bio = '',
    this.imagePath,
  }) : bornDateTime = DateFormat('d MMMM yyyy').parse(born),
       super(key: key);

  @override
  Widget build(BuildContext context) {
    // ...
    var t = AppLocalizations.of(context);
    return Card(
      child: Padding(
        // ...
        Padding(
          padding: const EdgeInsets.only(top: 2, bottom: 4),
          child: Text(
            born.isEmpty ? '' : t.heroBorn(bornDateTime),
            // ...
          ),
        ),
        // ...
      ),
    );
  }
}Code-Sprache: Dart (dart)

Wir bekommen das Geburtsdatum eines Helden als String, also müssen wir es zuerst in DateTime umwandeln. Dafür verwenden wir die intl DateFormat-Klasse im Widget-Konstruktor.

In der build-Methode übergeben wir einfach das geparste DateTime an unsere lokalisierte Nachricht t.heroBorn(). Das ergibt sauber lokalisierte Datumsangaben.

Englische Version der Alan Turing-Karte | Phrase
Arabische Version der Alan Turing-Karte | Phrase

Was ist, wenn du keines der vordefinierten Skelette verwenden und deine Datumsformate vollständig anpassen möchtest? Nun, ähnlich wie bei der Zahlenformatierung (siehe oben), müssten wir die Klasse intl.DateFormat direkt verwenden. Angenommen, wir möchten die Geburtsdaten unserer Helden in einem Format wie „1912-06-23“ anzeigen. Die Umsetzung würde wie folgt aussehen:

import 'package:intl/intl.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

// Im Widget-Builder mit Kontext
var t = AppLocalizations.of(context);
var bornDateTime = DateTime(1912, 6, 23);
var formattedBorn = DateFormat('yyyy-MM-dd', t.localeName).format(bornDateTime);
var message = t.heroBorn(formattedBorn);
// => "1912-06-23" in US-Englisch
// => "١٩١٢-٠٦-٢٣" im ägyptischen Arabisch
Code-Sprache: JavaScript (javascript)

Unsere lokalisierten heroBorn-Nachrichten in unseren ARB-Dateien würden dann einfach reguläre Object– oder String-Parameter verwenden, da wir die Formatierung bereits für diese vorgenommen haben.

Schreibrichtung: von links nach rechts und von rechts nach links

Während Englisch eine Von-links-nach-rechts-Sprache (LTR) ist, geht Arabisch in die andere Richtung und ist von rechts nach links (RTL) angeordnet. Das verursacht derzeit ein Problem für uns, wenn unsere App auf einem Gerät mit Arabisch als Systemsprache verwendet wird.

Arabische Version von Heroes of Computer Science mit fehlendem Padding | Phrase
Beachte das fehlende Padding zwischen Bildern und Text.

Das Bild und der Text in jeder Karte sind bündig, da wir EdgeInsets.only(right) verwenden, um den Abstand um unser Bild zu definieren.

import 'package:intl/intl.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

// Im Widget-Builder mit Kontext
var t = AppLocalizations.of(context);
var bornDateTime = DateTime(1912, 6, 23);
var formattedBorn = DateFormat('yyyy-MM-dd', t.localeName).format(bornDateTime);
var message = t.heroBorn(formattedBorn);
// => "1912-06-23" in US-Englisch
// => "١٩١٢-٠٦-٢٣" im ägyptischen Arabisch
Code-Sprache: Dart (dart)

Das funktioniert in LTR-Sprachen, wo wir einen rechten Rand zwischen dem Bild und dem Text wollen. In RTL-Sprachen hingegen wollen wir den Rand auf der linken Seite haben.

Eine einfache Lösung besteht darin, EdgeInsetsDirectional statt EdgeInsets zu verwenden.

//...

class HeroCard extends StatelessWidget {
  // ...

  @override
  Widget build(BuildContext context) {
    // ...
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(4.0),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: const EdgeInsetsDirectional.only(end: 8.0),
              child: ClipRRect(
                // Hochformatbild...
              ),
            ),
            Expanded(
              // Text-Widgets...
            ),
          ],
        ),
      ),
    );
  }
}Code-Sprache: PHP (php)

Beachte, dass wir end statt right verwenden, um den Abstand zwischen dem Bild und dem Text festzulegen. EdgeInsetsDirectional ist eines der wenigen Layout-Widgets von Flutter, die sich an der Schreibrichtung der Sprache orientieren. Diese Widgets verwenden die Parameter start und end anstelle von left und right. Und das Tolle ist, dass diese Widgets mit Richtungserkennung automatisch das Richtige für das aktive Gebietsschema tun:

  • start == left für LTR-Sprachen
  • start == right für RTL-Sprachen
  • end == right für LTR-Sprachen
  • end == left für RTL-Sprachen

Mit dieser kleinen Anpassung im Code ist unser Layout-Problem gelöst.

Arabische Version von Heroes of Computer Science mit Padding | Phrase
Das Layout passt sich jetzt der Richtung des aktiven Gebietsschemas an.

🔗 Quelle » Aktuell listet die offizielle Flutter-Dokumentation die folgenden Widgets mit Ausrichtung auf:

Mit all dem sieht unsere fertige App ziemlich globalisiert aus.

Arabische und englische Versionen von Heroes of Computer Science nebeneinander | Phrase

🔗 Quelle » Hol dir den vollständigen Code für unsere Demo-App von unserem GitHub-Repository.

Hinzufügen lokalisierter Assets

Die Lokalisierung von Bildern in Flutter umfasst die Verwendung verschiedener Bildsätze für unterschiedliche Sprachen oder Regionen in deiner App. Dies wird häufig gemacht, um Bilder mit Text oder Inhalten anzuzeigen, die deiner bevorzugten Sprache entsprechen.

Füge eine Flagge hinzu, die je nach Region des Nutzers unterschiedlich angezeigt wird.

Länderflagge zu lokalisierter Flutter-App hinzufügen | Phrase

Wir beginnen damit, die Bilder hinzuzufügen, eines für jede Region, die wir unterstützen wollen. Wir sollten diese Bilder in einer Ordnerstruktur basierend auf den Sprachen organisieren, zum Beispiel:

└── assets/
    └── images/
        ├── eg/
        │   └── flag.jpg
        ├── us/
        │   └── flag.jpg
        └── flag.jpgCode-Sprache: Klartext (plaintext)

Für dieses Tutorial werden wir Flaggenbilder für Ägypten und die USA hinzufügen. Die Datei direkt im images-Ordner (ohne Sprachversion) ist ein Fallback-Bild für andere Regionen.

Als nächstes registrieren wir die Dateien in pubspec.yml.

# pubspec.yml

# ...

flutter:
  # ...
  assets:
  # ...
    - assets/images/in/flag.jpg
    - assets/images/us/flag.jpg
    - assets/images/flag.jpg

# ...Code-Sprache: YAML (yaml)

Jetzt können wir die Bilder in der Datei hero_list.dart laden:


import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../widgets/hero_card.dart';

class HeroList extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    // ...
    String getImagePath(String imageName) {
      String basePath = 'assets/images/';

      // Wenn der Ländercode nicht unterstützt wird,
      // zeigen wir das Fallback-Bild an.
      if (locale.countryCode?.isEmpty == true) {
        return  basePath + 'flag.jpg';
      }

      String localePath = '${locale.countryCode!.toLowerCase()}/';
      return basePath + localePath + imageName;
    }

    return Scaffold(
      // ...
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
	    // ...
            Padding(
              padding: const EdgeInsets.only(bottom: 2.0),
              child: Image.asset(
                getImagePath('flag.jpg'),
                width: 40,
                height: 40,
              ),
            ),
          ],
        ),
      ),
    );
  }
}Code-Sprache: Dart (dart)

Nimm jetzt in der Datei main.dart die folgenden Änderungen vor.

...
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // Dieses Widget ist die Wurzel deiner Anwendung.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
    ...    
    supportedLocales: [
        const Locale('ar', ''),
        const Locale('en', ''),
				// Füge die unterstützten Region-Codes hinzu 
        const Locale('ar', 'EG'),
        const Locale('en', 'US'),
      ],
   ...
  }
}Code-Sprache: Dart (dart)

Jetzt zeigt die App das passende Asset für deine Sprache und Region an und zeigt eine Ersatzlösung, falls deine Sprache und Region nicht unterstützt werden.

Demo-App-Bildschirm, der das entsprechende Asset für die Locale des Benutzers und ein Fallback für nicht unterstützte Locales anzeigt | Phrase

Sprache in der App ändern

Manchmal möchtest du dein Betriebssystem in einer Sprache und eine bestimmte App in einer anderen Sprache haben. Um das zu ermöglichen, lass uns einen Sprachwähler in unsere App einbauen. Es sollte auf allen von Flutter unterstützten Plattformen funktionieren.

Demo-App-Bildschirm mit In-App-Sprachen | Phrase

In der Datei main.dart werden wir die folgenden Änderungen vornehmen:

void main() {
  runApp(const MyApp());
}

// Wir müssen MyApp Stateful machen, weil
// es reagieren muss, wenn sich die Locale ändert.
class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();

  static void setLocale(BuildContext context, Locale newLocale) {
    _MyAppState? state = context.findAncestorStateOfType<_MyAppState>();
    state?.setLocale(newLocale);
  }
}

class _MyAppState extends State<MyApp> {
  Locale? _locale;

  setLocale(Locale locale) {
    setState(() {
      _locale = locale;
    });
  }

  // Dieses Widget ist die Wurzel deiner Anwendung.
  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      // ...
      locale: _locale,
      initialRoute: '/',
      routes: {
        '/': (context) {
          return HeroList(title: AppLocalizations.of(context)!.appTitle);
        },
      },
    );
  }
}Code-Sprache: Dart (dart)

Jetzt, in der Datei hero_list.dart, erstelle das Dropdown-Menü und ruf die Methode setLocale() auf, die wir oben definiert haben.

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../widgets/hero_card.dart';

class HeroList extends StatelessWidget {
  // ...
  @override
  Widget build(BuildContext context) {
    var t = AppLocalizations.of(context)!;
    final Locale locale = Localizations.localeOf(context);
    // Dropdown-Optionen
    var items = [
      'en',
      'ar',
    ];

    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        actions: <Widget>[
          DropdownButton(
            // Abwärtspfeil-Icon
            icon: const Icon(Icons.settings, color: Colors.white,),
            items: items.map((String items) {
              return DropdownMenuItem(
                value: items,
                child: Text(items),
              );
            }).toList(),
            onChanged: (String? newValue) {
              MyApp.setLocale(context, Locale(newValue));
            },
          ),
        ],
      ),
      // ...
    );
  }
}Code-Sprache: Dart (dart)

Damit kannst du die App-Sprache unabhängig von der Systemsprache auswählen.

🗒️ Hinweis » Bevor du ein Gebietsschema auswählst, wird es auf das Standard-Gebietsschema deines Systems gesetzt.

Simulator-App-Bildschirmaufnahme auf iPhone 14 Plus | Phrase

🗒️ Hinweis » Bevor du in der App manuell ein Gebietsschema auswählst, wird das System-Gebietsschema standardmäßig verwendet. Die von dir manuell in der App ausgewählte Sprache wird nicht beibehalten, wenn die App neu gestartet wird. Um deine Spracheneinstellung in der App beizubehalten, musst du das Gebietsschema mit etwas wie dem Shared Preferences Plugin speichern und abrufen.

Das fertige Produkt findest du auf GitHub.

Flutter-Lokalisierung leicht gemacht

Wir hoffen, dir hat unser Flutter-Lokalisierungstutorial gefallen und du hast ein paar nützliche Tricks gelernt. Wenn du bereit bist, dein Lokalisierungs-Game aufs nächste Level zu heben, ist Phrase Strings genau die richtige Lösung für dich. Wie ein Lokalisierungsassistent für deine Flutter-Apps unterstützt Phrase Strings ARB-Dateien und ist dank seiner einfach zu bedienenden API und CLI äußerst entwicklerfreundlich. Es gibt auch einen eleganten Strings-Editor, der das Übersetzen zum Kinderspiel macht.

Außerdem synchronisiert es sich nahtlos mit GitHub, GitLab und Bitbucket und bietet sogar Over-the-Air-Übersetzungen für mobile Apps, um die Lokalisierung für dich zu übernehmen – damit du dich auf den Code konzentrieren kannst, den du so sehr liebst. Schau dir alle Phrase-Funktionen für Entwickler an und überzeuge dich selbst, wie sie dir helfen können, deine Apps schneller global zu machen.

Verwandte Beiträge

Blog post

Die Lokalisierung von Unity-Spielen mit dem offiziellen Phrase Plug-in

Willst du dein Unity-Spiel ohne das CSV-Chaos lokalisieren? Entdecke, wie das offizielle Phrase Strings Unity-Plugin deinen Lokalisierungs-Workflow vereinfacht – vom Setup der Strings-Tabelle bis zum direkten Abrufen von Übersetzungen in dein Projekt. Egal, ob du für Deutsch, Serbisch oder andere Sprachen entwickelst – dieser Leitfaden zeigt dir, wie du schnell loslegst und wie ein Profi lokalisierst.

Python localization blog post featured image | Phrase

Blog post

Der ultimative Guide zur Python-Lokalisierung

Mit dem Schritt-für-Schritt-Leitfaden zur Vorbereitung von Python-Apps für die Lokalisierung und den gängigen Modulen kann mehrsprachige Unterstützung für eine globale Benutzerbasis implementiert werden.

Vue localization blog post featured image | Phrase

Blog post

Ein umfassender Leitfaden zur Lokalisierung mit Vue

In diesem Leitfaden zur Vue-Lokalisierung erläutern wir, wie die Vue I18n Bibliothek in die App integriert werden kann, um sie für Nutzer weltweit zugänglich zu machen.

Software localization blog category featured image | Phrase

Blog post

JavaScript-Lokalisierung – der ultimative Leitfaden

Starte deine Browser JavaScript-Lokalisierung mit diesem umfassenden Leitfaden und mach deine Anwendung bereit für internationale User.

Software localization blog category featured image | Phrase

Blog post

So wird das Lokalisierungs-Plugin von Phrase Strings für WordPress verwendet

Im Folgenden zeigen wir, wie WordPress-Seiten, -Beiträge und mehr mit der Integration von Phrase Strings für WordPress in mehrere Sprachen übersetzt werden können.