Software-Lokalisierung

Ein umfassender Leitfaden zur Vue-Lokalisierung

Tauche ein in die Vue-Lokalisierung und lerne, wie du die Vue I18n-Bibliothek in deine App integrieren kannst, damit du sie einem globalen Benutzerkreis zugänglich machen kannst.
Vue localization blog post featured image | Phrase

Unter den großen drei UI-Frameworks ist Evan You’s Vue wahrscheinlich das zugänglichste und scheint ein unwahrscheinlicher Konkurrent gegen die Giganten Meta’s React und Google’s Angular zu sein. Doch diese Idee eines Einzelnen hat eine Verbreitung, die mit der von Angular mithalten kann, dank der sanften Lernkurve, erstklassigen Entwicklererfahrung und produktionsbereiten Funktionen.

Mit seiner Popularität hat Vue ein reichhaltiges Ökosystem von Plugins, Erweiterungen und Diensten hervorgebracht. Die Internationalisierung von Vue-Apps (i18n) — vermutlich der Grund, warum du hier bist — sieht das robuste Vue I18n Plugin von Drittanbietern als die offensichtliche erste Wahl. In diesem praktischen Leitfaden werden wir Vue I18n verwenden, um eine kleine Demo-App zu internationalisieren, und alles abdecken, was du benötigst, um mit der Vue-Lokalisierung zu beginnen. Los geht's.

Achtung » In diesem Artikel geht's um die Lokalisierung von Vue 3. Interessierst du dich für Vue 2? Dann schau dir Vue 2 Lokalisierung mit Vue I18n an: Ein Schritt-für-Schritt-Guide.

🔗 Ressource » In diesem Artikel nutzen wir die Vue I18n-Bibliothek. Möchtest du lieber i18next verwenden? Dann könnte unser Tiefer Einblick: Vue-Übersetzung mit vue-i18next interessant für dich sein.

Verwendete Bibliotheks-Versionen

Wir haben in diesem Artikel die folgenden NPM-Pakete verwendet (Versionen in Klammern).

  • Vue (3.2) — unser UI-Framework
  • Vue Router (4.1) — der offizielle SPA-Router von Vue
  • Vue I18n (9.2) — die Drittanbieter-Bibliothek für i18n von Vue
  • Tailwind CSS (3.1) — fürs Styling genutzt und optional für unsere Zwecke

🗒 Hinweis » Um den Fokus auf die i18n zu legen, zeigen wir in diesem Artikel kein CSS-Styling. Du findest den gesamten Styling-Code im vollständigen Code unseres Artikels auf GitHub.

Unsere Demo

Unsere bescheidene Demo, Mushahed, basiert auf Daten von den Open Notify Space-APIs.

Unsere Demo-App feiert die mutigen Astronauten weltweit

Unsere Demo-App feiert die mutigen Astronauten weltweit

Attributionen

Ein fettes Dankeschön an die folgenden Leute und Organisationen, die ihre Sachen kostenlos rausgehauen haben.

Unsere Demo ist eine Vue SPA, die mit npm init vue@latest gestartet wurde. Wir haben Router-Unterstützung hinzugefügt und uns gegen TypeScript entschieden, als das Projekt erstellt wurde. Nachdem wir die Boilerplate-Komponenten, die vom Scaffold-Tool hinzugefügt wurden, entfernt haben, haben wir diese kleine Hierarchie erstellt:

.

└── src/

    ├── components/

    │   ├── AstroCard.vue

    │   ├── Astronauts.vue

    │   ├── Coords.vue

    │   ├── Footer.vue

    │   └── Nav.vue

    ├── router/

    │   └── index.js

    ├── views/

    │   ├── HomeView.vue

    │   └── AboutView.vue

    └── App.vue



Die Komponentenübersicht unserer Demo
Die Komponentenübersicht von unserer Demo

🗒 Hinweis » Unsere App enthält eine Vue <RouterView> und wir verwenden <RouterLink>s in unserem Nav. Wir schauen uns das Routing später an.

Schauen wir uns mal genauer unsere <Astronauten>-Komponente an.

<script>

import AstroCard from './AstroCard.vue'



export default {

  components: { AstroCard },



  data() {

    return {

      loading: true,

      astros: [],

    }

  },



  created() {

    fetch('/data/astronauts.json')

      .then((res) => res.json())

      .then((data) => {

        this.astros = data

        this.loading = false

      })

  },

}

</script>



<template>

  <!-- No i18n: Fest kodiertes Englisch -->

  <p v-if="loading">Laden...</p>



  <div v-else>

    <div>

      <h2>

        <!-- No i18n: Fest codierter Plural -->

        🧑‍🚀 {{ astros.length }} Leute im All

      </h2>



      <p>

       <!-- No i18n: Fest eingetragenes Datum -->

        Aktualisiert am 26. Juli 2022

      </p>

    </div>



    <div>

      <AstroCard

        v-for="astro in astros"

        :key="astro.id"

        :name="astro.name"

        :nationality="astro.nationality"

        :craft="astro.craft"

        :photoUrl="astro.photoUrl"

      />

    </div>

  </div>

</template>



Wenn <Astronauten> erstellt wird, laden wir unsere Astronautendaten von public/data/astronauts.json und geben sie an Instanzen der präsentierenden <AstroCard> weiter.

[

  // ...



  {

    "id": 7,

    "name": "Jessica Watkins",

    "photoUrl": "j-watkins.jpg",

    "nationality": "USA 🇺🇸",

    "craft": "ISS"

  },



  // ...

]



Unsere <AstroCard>-Instanzen rendern Astronautendaten
Unsere <AstroCard>-Instanzen rendern Astronautendaten

Beachte, dass unsere UI-Strings zu diesem Zeitpunkt alle fest in Englisch codiert sind. Lass uns das angehen und unsere App lokalisieren.

🔗 Ressource » Wir lassen einen Großteil unseres Demo-Startercodes der Kürze halber weg. Du kannst alles davon aus dem start-options-Branch unseres GitHub-Repos holen.

Wie installiere und richte ich Vue I18n ein?

Das wird dich schockieren: Wir starten mit einer NPM-Installation in der Kommandozeile aus dem Stammverzeichnis unseres Vue-Projekts.

$ npm install vue-i18n@9 



🗒 Hinweis » Du brauchst v9+ von Vue I18n, wenn du mit Vue 3 arbeitest. Vue 2 nutzt Vue i18n v8.

🔗 Ressource » Schau dir alle Möglichkeiten zur Installation von Vue I18n in der offiziellen Dokumentation an.

Sobald NPM sein Ding gemacht hat, müssen wir eine Vue I18n-Instanz erstellen, sie konfigurieren und sie als Plugin bei unserer Vue-Instanz anmelden. Lass uns die Vue I18n-Instanz in einem neuen Modul konstruieren. Wir legen ein Verzeichnis namens src/i18n an und platzieren eine index.js-Datei darin.

import { createI18n } from 'vue-i18n'



const i18n = createI18n({

  // Standard-Region

  locale: 'en',



  // Übersetzungen

  messages: {

    en: {

      appTitle: 'Mushahed',

    },

    ar: {

      appTitle: 'مشاهد',

    },

  },

})



export default i18n



Wir leiten unsere Übersetzung messages an das i18n-Objekt weiter, das wir mit createI18n() erstellen. Die anfängliche Locale, auf die unsere App beim ersten Laden standardmäßig zurückgreift, wird über die locale Konfigurationsoption festgelegt.

🗒 Hinweis » Ich unterstütze Englisch (en) und Arabisch (ar) in meiner App. Fühl dich frei, hier beliebige Sprachen zu unterstützen. Verwende ein standardmäßiges BCP 47 Sprach-Tag (wie en) oder ein Sprach-Tag mit einem Regionsuntertag (wie en-US), um deine Übersetzungs-Lokalisierungen zu identifizieren.

🔗 Ressource » Alle Konfigurationsoptionen für createI18n() findest du in der offiziellen API-Dokumentation.

Unsere Vue-Instanz muss jetzt unser i18n Objekt als Plugin mit einem use() Aufruf registrieren.

import { createApp } from 'vue'

import App from './App.vue'

import router from './router'

import i18n from './i18n'



import './assets/main.css'



const app = createApp(App)



app.use(i18n)

app.use(router)



app.mount('#app')



Das sollte unser Setup komplett machen. Testen wir unser i18n, indem wir den App-Titel in unserer <Nav>-Komponente internationalisieren.

<script setup>

import { RouterLink } from 'vue-router'

</script>



<template>

  <nav>

    <img alt="Mushahed logo" src="@/assets/logo.svg" />



    <!-- Der App-Titel ist fest auf Englisch eingestellt -->

    <span>Mushahed</span>



    <RouterLink to="/">Startseite</RouterLink>

    <RouterLink to="/about">Über uns</RouterLink>

  </nav>

</template>



Wir werden den fest eingetragenen Text durch Folgendes ersetzen.

<script setup>

import { RouterLink } from 'vue-router'

</script>



<template>

  <nav>

    <!-- ... -->



    <span>{{ $t('appTitle') }}</span>



    <!-- ... -->

  </nav>

</template>



Jetzt steht allen unseren Komponenten die Übersetzungsfunktion $t() von Vue I18n zur Verfügung. Wenn $t('appTitle') aufgerufen wird, während die aktive Sprache Englisch (en) ist, gibt $t() die Nachricht zurück, die wir oben bei messages.en.appTitle angegeben haben. Wenn die aktive Sprache Arabisch (ar) ist, wird messages.ar.appTitle angezeigt.

🤿 Tauch tiefer ein » Schau dir die unzähligen Möglichkeiten an, $t() in der offiziellen API-Liste zu nutzen.

Wenn wir unsere App jetzt neu laden, sollten wir keine Veränderung sehen: Das liegt daran, dass unsere ursprüngliche Sprache auf Englisch eingestellt ist. Lass uns auf Arabisch wechseln.

import { createI18n } from 'vue-i18n'



const i18n = createI18n({

  locale: 'ar',

  messages: {

	en: {

      appTitle: 'Mushahed',

    },

    ar: {

      appTitle: 'مشاهد',

    },

  },

})



export default i18n



Et voilà!

Unser App-Name übersetzt ins Arabische

Unser App-Name übersetzt ins Arabische

Das ist alles, was du brauchst, um mit Vue I18n in unseren Apps loszulegen. Natürlich wollen wir wahrscheinlich weiterhin Übersetzungsnachrichten hinzufügen, während wir unsere App weiterentwickeln. Um die Dinge ordentlich zu halten, lass uns unser messages Objekt in ein eigenes Modul umstrukturieren.

export default {

  en: {

    appTitle: 'Mushahed',

  },

  ar: {

    appTitle: 'مشاهد',

  },

}



import { createI18n } from 'vue-i18n'

importiere messages von './messages'



const i18n = createI18n({

  locale: 'ar',

  messages,

})



export default i18n



🗒 Hinweis » Beim Entwickeln mit Vue I18n bekommst du möglicherweise eine Warnung in deiner Browser-Konsole, die besagt: „Du verwendest den esm-bundler-Build von vue-i18n…“. Dies ist ein bekanntes Problem und könnte behoben sein, wenn du dies liest.

Wie übersetze ich Nachrichten in meinen Komponenten?

Wir haben das schon mal angesprochen, als wir unsere Vue I18n-Installation oben getestet haben, aber es lohnt sich, es nochmal zu sagen. Du brauchst nur zwei Schritte:

  1. In unserem messages-Objekt fügen wir unter jedem unserer Locales Übersetzungen mit einem gemeinsamen Schlüssel hinzu.
  2. Wir verwenden $t(key) in unseren Komponenten-Templates, um die Übersetzung darzustellen, die dem aktiven Gebietsschema entspricht.

Lass uns das anwenden, indem wir den Rest unserer <Nav> Komponente lokalisieren. Wir brauchen noch ein paar Übersetzungen, um loszulegen.

export default {

  en: {

    // Verwende dieselben Schlüssel wie ar

    appTitle: 'Mushahed',

    logo: 'Mushahed-Logo',

    home: 'Home',

    about: Über

  },

  ar: {

    // Verwende dieselben Schlüssel wie en

    appTitle: 'مشاهد',

    logo: 'رمز مشاهد',

    home: 'الرئيسية',

    about: 'Über uns',

  },

}



<script setup>

import { RouterLink } from 'vue-router'

</script>



<template>

  <nav>

    <img :alt="$t('logo')" src="@/assets/logo.svg" />

    <span>{{ $t('appTitle') }}</span>



    <RouterLink to="/">{{ $t('home') }}</RouterLink>

    <RouterLink to="/about">{{ $t('about') }}</RouterLink>

  </nav>

</template>



Wir können $t() mit der {{ }} Syntax verwenden, um den inneren Text eines Elements zu übersetzen. Beim Übersetzen eines Attributs ist die :attribute Bindungsabkürzung echt praktisch.

Unsere App-Name auf Arabisch übersetzt

Unsere Navigationskomponente, wenn Arabisch die aktive Sprache ist

Wie gehe ich mit dynamischen Werten in meinen Übersetzungsnachrichten um?

Ein häufiger Anwendungsfall: Das Interpolieren von Werten, die sich zur Laufzeit in unseren Nachrichten ändern, ist mit Vue I18n einfach. Unsere <Coords> Komponente, die die Koordinaten der Internationalen Raumstation (ISS) zu einem bestimmten Zeitpunkt zeigt, ist perfekt, um das zu demonstrieren.

<script>

export default {

  data() {

    return {

      coords: {

        Breitengrad: 49.5908,

        Längengrad: 122.8927,

      },

      datetime: new Date(1658828129000),

    }

  },

}

</script>



<template>

  <p>

    Die ISS war über {{ coords.latitude }}° N, {{ coords.longitude }}° E am {{ datetime }}

  </p>

</template>



Hard-coded English Interpolation

Wir haben die Koordinaten- und Datumswerte oben zur Klarheit fest codiert, aber in einer echten App würden diese wahrscheinlich von einer API abgerufen und beim Erstellen der Komponente aktualisiert. Vue I18n berücksichtigt diese dynamischen Werte in seinen Nachrichten über eine {placeholder} Syntax.

export default {

  en: {

    // ...

    issPosition: 'Die ISS war über {latitude}° N, {longitude}° E am {datetime}.'

  },

  ar: {

    // ...

    issPosition:

      'كانت محطة الفضاء الدولية فوق {latitude} درجة شمالا و {longitude} درجة شرقا يوم {datetime}',

  },

}



<script>

export default {

  data() {

    return {

      coords: {

        Breitengrad: 49.5908,

        Längengrad: 122.8927,

      },

      datetime: new Date(1658828129000),

    }

  },

}

</script>



<template>

  <p>

    {{ $t('issPosition', {

        latitude: coords.latitude,

        longitude: coords.longitude,

        datetime,

      }) }}

  </p>

</template>



Wenn du ein zweites Argument an $t() übergibst — eine Map von Schlüssel/Wert-Paaren, bei denen die Schlüssel mit denen in unseren Übersetzungsnachrichten übereinstimmen — werden diese Nachrichten mit den eingefügten Werten gerendert.

Eine arabische Nachricht mit eingefügten dynamischen Werten

Eine arabische Nachricht mit interpolierten dynamischen Werten

🗒 Hinweis » Die Zahlen und das Datum oben sind nicht in Arabisch. Wir kümmern uns später darum.

🤿 Tauch tiefer ein » Erfahre alle Möglichkeiten, wie du in Nachrichten interpolieren kannst, in der offiziellen Vue I18n-Dokumentation.

Wie kann ich Strings in meinem Component-JavaScript übersetzen?

Die $t()-Funktion ist über this.$t() in unserem Komponenten-JavaScript verfügbar. Lass uns das nutzen, um unsere <Coords>-Komponente zu refaktorisieren und diesen umfangreichen $t()-Aufruf in unser Komponenten-<script> zu verschieben.

<script>

export default {

  data() {

    return {

      coords: {

        Breitengrad: 49.5908,

        Längengrad: 122.8927,

      },

      datetime: new Date(1658828129000),

    }

  },



  computed: {

    issPosition() {

      return this.$t('issPosition', {

        latitude: this.coords.latitude,

        longitude: this.coords.longitude,

        datetime: this.datetime,

      })

    },

  },

}

</script>



<template>

  <p>{{ issPosition }}</p>

</template>



Wie arbeite ich mit HTML in meinen Übersetzungsnachrichten?

Gelegentlich müssen wir HTML in unsere Übersetzungsnachrichten einbauen. Unser <Footer> ist ein gutes Beispiel.

<template>

  <p>

    Erstellt mit

    <a href="https://vuejs.org/">Vue</a> für eine

    <a href="https://phrase.com/blog">Phrase blog</a>

    Anleitung.

  </p>

</template>



Es ist knifflig, diesen Text zu lokalisieren, weil die Positionen der eingebetteten Links je nach Übersetzungssprache variieren können. Wir könnten die <a> Tags direkt in unsere Übersetzungsnachrichten einfügen und die unsichere v-html Direktive von Vue nutzen, um die Übersetzungen auszugeben. Das könnte uns aber XSS-Angriffen aussetzen, wenn wir nicht aufpassen.

Vue I18n bietet eine bessere Lösung: Mit seiner <i18n-t> Komponente können wir seine Kinder, einschließlich HTML-Elemente, innerhalb unserer Nachrichten über Platzhalter darstellen.

export default {

  en: {

    // ...

    footer: 'Erstellt mit {0} für eine {1}.'

    vue: 'Vue',

    phraseBlogTutorial: 'Phrase-Blog-Tutorial'

  },

  ar: {

    // ...

    footer: '.تم إنشائه بواسطة {0} ليصاحب {1}',

    vue: 'ڤيو',

    phraseBlogTutorial: 'درس على مدونة فريز',

  },

}



<script>

export default {

  data() {

    return {

      vueUrl: 'https://vuejs.org/',

      phraseBlogUrl: 'https://phrase.com/blog',

    }

  },

}

</script>



<template>

  <i18n-t keypath="footer" tag="p" scope="global">

    <a :href="vueUrl">{{ $t('vue') }}</a>

    <a :href="phraseBlogUrl">{{ $t('phraseBlogTutorial') }}</a>

  </i18n-t>

</template>



Wir geben <i18n-t> eine keypath Eigenschaft mit dem Schlüssel unserer Hauptübersetzungsnachricht, in diesem Fall footer. Für das Rendering sagen wir <i18n-t>, dass wir ein umgebendes <p> über die tag-Eigenschaft ausgeben wollen.

In unserer Hauptnachricht legen wir Platzhalter mit list interpolation fest, das heißt, wir fangen mit {0} an und gehen dann zu {1} usw. weiter. Hier zählt die Reihenfolge: Das erste <a> in <i18n-t> ersetzt den {0} Platzhalter, das zweite ersetzt {1}, und so weiter. So können wir bestimmen, in welcher Reihenfolge unsere HTML-Elemente in der Übersetzungsnachricht jeder Sprache auftauchen.

🗒 Hinweis » Wenn wir die scope="global" Eigenschaft nicht explizit auf der <i18n-t> Komponente setzen, kriegen wir eine Konsolenwarnung, die sagt: „[intlify] Übergeordneten Bereich nicht gefunden. Nutze den globalen Bereich.“

🔗 Ressource » Der Abschnitt Component Interpolation im offiziellen Leitfaden geht genauer auf die <i18n-t> Komponente ein.

Wie gehe ich mit Pluralen in meinen Übersetzungen um?

Die beiden englischen Pluralformen sind einfach: “a satellite is umkreist die Erde”; “three satellites are umkreisen die Erde”. Andere Sprachen sind komplizierter. Einige haben vier Pluralformen. Walisisch und Arabisch haben sechs. Vue I18n behandelt einfache Pluralformen, wie die im Englischen, direkt nach der Installation. Wir können das Plugin erweitern, um mit komplexen Pluralformen umzugehen. Wir gehen hier sowohl auf einfache als auch auf komplexe Pluralformen ein.

🔗 Ressource » Die CLDR Language Plural Rules Referenz ist die Richtlinie für die Pluralformen von Sprachen.

Schauen wir uns den Header unserer <Astronauts>-Komponente nochmal an.

Ein Astronauten-Zähler

Ein Astronauten-Zähler

<script>

// Hier füllen wir das astros-Array auf...

</script>

<template>

  <div>

    <div>

      <h2>🧑‍🚀 {{ astros.length }} Leute im Weltraum</h2>



      <p>Aktualisiert am 26. Jul 2022</p>

    </div>



    <!-- ... -->

  </div>

</template>



Unser Astronautenzähler ist fest codiert und bereit für die Lokalisierung. Lass uns eine englische Nachricht dafür hinzufügen.

export default {

  en: {

    // ...

    peopleInSpace:

      '{n} Person im All | {n} Leute im All',

  },

  ar: {

    // ...

  }

}



Vue I18n erwartet, dass die Pluralformen durch ein Pipe (|)-Zeichen getrennt werden. Wir haben die beiden Pluralformen für Englisch oben angegeben. Der {n} Platzhalter wird durch einen ganzzahligen Zähler ersetzt, wenn wir die Pluralnachricht abrufen.

<script>

// Wir befüllen hier das astros-Array...

</script>

<template>

  <div>

    <div>

      <h2>🧑‍🚀 {{ $tc('peopleInSpace', astros.length) }}</h2>



      <p>Aktualisiert am 26. Juli 2022</p>

    </div>



    <!-- ... -->

  </div>

</template>



$tc(), eine weitere Übersetzungsfunktion, die von Vue i18n in all unseren Komponenten eingefügt wird, wählt die passende Pluralform basierend auf ihrem zweiten Parameter, dem Integer-Zähler.

Darstellungen der englischen Pluralformen. Beachtet, dass {n} durch unseren Zähler ersetzt wird.

Darstellungen von englischen Pluralformen. Beachtet, dass {n} durch unseren Zähler ersetzt wird.

Zwei Formen klappen gut für Englisch, aber unsere arabische Übersetzung braucht sechs Pluralvarianten.

export default {

  en: {

    // ...

  },

  ar: {

    // ...

    peopleInSpace:

      'لا يوجد أحد في الفضاء | يوجد شخص {n} في الفضاء | يوجد شخصان في الفضاء | توجد {n} أشخاص في الفضاء | يوجد {n} شخص في الفضاء | يوجد {n} شخص في الفضاء'

  },

}



Für sich allein funktioniert Vue I18n nur mit Pluralformen, die dem Englischen ähneln, also müssen wir eine benutzerdefinierte Erweiterungsfunktion hinzufügen, die die sechs Formen des Arabischen handhabt.

export function arabischePluralRegeln(auswahl) {

  const name = new Intl.PluralRules('ar').select(choice)



  return { zero: 0, eins: 1, zwei: 2, wenige: 3, viele: 4, andere: 5 }[name]

}



Das Standard Intl.PluralRules Objekt von JavaScript meistert komplexe Plurale wunderbar. Wir müssen ihm nur ein Locale beim Erstellen geben und dann seine select()-Methode mit einem Integer-Zähler aufrufen. Die Methode liefert den Namen der passenden Form für die jeweilige Sprache. Zum Beispiel liefert new Intl.PluralRules('ar').select(5) die passende few Form.

Vue I18n benötigt einen Integer-Index, um die passende Form in unseren Übersetzungsnachrichten auszuwählen. Daher muss unser individueller Pluralwähler den CLDR Pluralformnamen (few) auf einen nullbasierten Index (3) abbilden. Der Index wählt aus unserer mit | getrennten Nachricht aus. Also würde 3 unsere vierte Variante aus der peopleInSpace-Nachricht oben auswählen.

Alles, was wir jetzt tun müssen, ist, unseren arabischen Pluralregelwähler beim Erstellen der Vue I18n Instanz anzuschließen.

importiere { createI18n } von 'vue-i18n'

importiere messages von './messages'

importiere { arabicPluralRules } von './plurals'



const i18n = createI18n({

  locale: 'ar',

  messages,

  // Vue I18n ermöglicht uns, seine Pluralisierung zu erweitern

  // Formatierung durch Bereitstellung eines Formselektors

  // Funktion pro Locale

  pluralizationRules: {

    de: deutschePluralregeln,

  },

})



exportiere standardmäßig i18n



Mit unserem verbundenen Selektor sollten unsere arabischen Pluralformen wie ein Charme funktionieren.

Darstellungen arabischer Pluralformen. Beachte, dass {n} durch unseren Zähler ersetzt wird.

Darstellungen arabischer Pluralformen. Beachte, dass {n} durch unseren Zähler ersetzt wird.

Hinweis » Du hast vielleicht bemerkt, dass der interpolierte Zähler in westlichen arabischen Ziffern (1, 2 usw.) angezeigt wird. Allerdings verwendet Arabisch östliche arabische Ziffern (١، ٢، ٣، usw.). Obwohl es kein Showstopper ist, habe ich dieses Problem auf dem Vue i18n GitHub protokolliert, falls du es verfolgen möchtest.

🔗 Ressource » Erfahre mehr im offiziellen Leitfaden zur Pluralisierung.

Wie formatiere ich lokalisierte Zahlen?

Verschiedene Sprachregionen verwenden unterschiedliche Zahlensysteme, Tausendertrennzeichen und Symbole, um Zahlen darzustellen. Das eingebaute Intl.NumberFormat Objekt von JavaScript kümmert sich um all das für uns und wird im Hintergrund von Vue I18n verwendet. Wir müssen Vue I18n nur vorkonfigurierte Zahlenformate geben, die das Plugin wiederum an Intl.NumberFormat weitergibt. Die Formate, die wir registriert haben, sind dann in unseren Komponenten verfügbar.

🤿 Geh tiefer » Unser Kompakter Leitfaden zur Zahlenslokalisierung behandelt Zahlensysteme, Trennzeichen und mehr.

// Wir geben die Formate an, die unsere App nutzen wird

export const numberFormats = {

  'en-US': {

    // Ein benanntes Format

    coords: {

      // Diese Optionen werden an Intl.NumberFormat weitergegeben

      style: 'decimal',

      minimumSignificantDigits: 6,

      maximumSignificantDigits: 6,

    },

  },

  'ar-EG': {

    coords: {

      style: 'decimal',

      minimumSignificantDigits: 6,

      maximumSignificantDigits: 6,

    },

  },

}



Wir müssen unsere Zahlenformate während der Konstruktion beim Vue I18n-Objekt registrieren.

import { createI18n } aus 'vue-i18n'

importiere messages von './messages'

import { numberFormats } from './numbers'

import { arabicPluralRules } aus './plurals'



const i18n = createI18n({

  locale: 'en-US',

  messages,

  numberFormats,

  pluralizationRules: {

    'ar-EG': arabicPluralRules,

  },

})



exportiere standard i18n



Jetzt können wir die eingefügte $n() Funktion nutzen, um lokalisierte Zahlen in unseren Komponentenvorlagen zu formatieren.

<!-- Wir geben das benannte Format als zweiten Parameter an -->

<p>{{ $n(49.5908, 'coords') }}</p>



<!-- => "49.5908" wenn die Locale en-US ist -->

<!-- => "٤٩،٥٩٠٨" when locale is ar-EG -->



Achtung » Vielleicht ist dir aufgefallen, dass wir en mit en-US und ar mit ar-EG in unserer Konfiguration oben ausgetauscht haben. Das liegt daran, dass die Zahlenformatierung regionsspezifisch und nicht sprachspezifisch ist. Wenn wir Länder oder Regionen zu unseren Locale-Tags hinzufügen, können wir die Ausgabe der lokalisierten Zahlenformatierung steuern. Sonst besteht die Gefahr, dass der Browser eine Standardregion verwendet. Klar, wir müssen auch unsere Nachrichten updaten, so dass sie mit en-US und ar-EG verknüpft sind.

export default {

  'en-US': {

    appTitle: 'Mushahed',

    // ...

  },

  'ar-EG': {

    appTitle: 'مشاهد',

    // ...

  },

}



Lass uns unsere <Coords> Komponente aktualisieren, um die ISS-Koordinaten im Zahlenformat der aktiven Locale zu formatieren.

<script>

export default {

  data() {

    return {

      coords: null,

      datetime: '',

    }

  },



  created() {

    // Wir holen die Koordinatendaten und setzen sie

    // this.coords und this.datetime hier...

  },



  computed: {

    issPosition() {

      return this.$t('issPosition', {

        latitude: this.$n(this.coords.latitude, 'coords'),

        Längengrad: this.$n(this.coords.longitude, 'coords'),

        Datum und Uhrzeit: this.datetime,

      })

    },

  },

}

</script>



<template>

  <p>{{ issPosition }}</p>

</template>



Darstellungen arabischer Pluralformen. Beachte, dass {n} durch unseren Zähler ersetzt wird.

Darstellungen arabischer Pluralformen. Beachte, dass {n} durch unseren Zähler ersetzt wird.

🔗 Ressource » Hol dir mehr Details aus dem Nummernformatierungs Abschnitt der Vue I18n-Dokumentation.

Das Datum oben sieht in der ansonsten arabischen Nachricht sehr englisch aus, oder? Keine Sorge. Rate mal, was als Nächstes kommt?

Wie formatiere ich lokalisierte Datum- und Zeitangaben?

Ähnlich wie bei der Nummernformatierung ist die Datumsformatierung regionspezifisch. Die USA und Kanada sprechen beide Englisch, aber der 9. September 2022 kann in den USA 9/4/2022 und in Kanada 2022-09-04 sein. Um korrekt mit lokalisierten Datum- und Zeitangaben zu arbeiten, folgen wir einem Rezept, ähnlich wie bei Datumsangaben. Wir stellen Vue I18n benannte Datumsformatierungen zur Verfügung, die das Plugin als Optionen an das Standard-Intl.DateTimeFormat übergibt. Wir verwenden dann diese registrierten Formate in unseren Komponenten.

🗒 Hinweis » Die Datums-Lokalisierung ist der Nummern-Lokalisierung sehr ähnlich, daher baut dieser Abschnitt auf dem letzten auf.

export const datetimeFormats = {

  'en-US': {

    full: {

      // Diese Optionen werden an Intl.DateTimeFormat übergeben

      dateStyle: 'full',

      timeStyle: 'full',

    },

    kurz: {

      year: 'numeric',

      month: 'short',

      day: 'numeric',

    },

  },

  'ar-EG': {

    full: {

      dateStyle: 'full',

      timeStyle: 'full',

    },

    kurz: {

      year: 'numeric',

      month: 'long',

      day: 'numeric',

    },

  },

}



import { createI18n } from 'vue-i18n'

import messages from './messages'

import { numberFormats } from './numbers'

import { datetimeFormats } from './datetimes'

import { arabicPluralRules } from './plurals'



const i18n = createI18n({

  locale: 'en-US',

  messages,

  numberFormats,

  datetimeFormats,

  pluralizationRules: {

    'ar-EG': arabicPluralRules,

  },

})



export default i18n



Mit den spezifizierten und registrierten Formaten können wir die eingefügte $d() Funktion nutzen, um lokalisierte Daten in unseren Komponenten darzustellen. Lass uns unsere <Coords> Komponente mit ordentlicher Datumsformatierung abrunden.

<script>

export default {

  // ...



  computed: {

    issPosition() {

      return this.$t('issPosition', {

        latitude: this.$n(this.coords.latitude, 'coords'),

        Längengrad: this.$n(this.coords.longitude, 'coords'),

        datum: this.$d(this.datetime, 'full'),

      })

    },

  },

}

</script>



<template>

  <p>{{ issPosition }}</p>

</template>



Amerikanisches Englisch und Ägyptisches Arabisch vollständige Datumsformate

Amerikanisches Englisch und ägyptisches Arabisch: vollständige Datumsformate

Während wir dabei sind, lass uns unseren <Astronauten> Header formatieren, um lokalisierte kurze Daten in seiner "Aktualisiert"-Nachricht anzuzeigen.

export default {

  'en-US': {

    // ...

    updatedAt: 'Aktualisiert am {date}',

  },

  'ar-EG': {

    // ...

    aktualisiertAm: 'Letzte Aktualisierung {date}',

  },

}



<script>

// ...

</script>

<template>

  <!-- ... -->

  <h2>🧑‍🚀 {{ $tc('peopleInSpace', astros.length) }}</h2>

  <p>

    {{ $t('updatedAt', { date: $d(updated, 'short') }) }}

  </p>

</template>



Amerikanische und ägyptische Kurzdatumsformate dargestellt

Amerikanische und ägyptische Kurzdatumsformate dargestellt

🔗 Ressource » Der offizielle Vue I18n Guide behandelt mehr Datumsformatierungsoptionen.

Wie kriege ich die aktive Spracheinstellung raus?

Manchmal müssen wir Entscheidungen treffen, die auf der Laufzeit-Locale der App basieren. Mit Vue I18n kriegen wir die aktive Sprache einfach über i18n.locale raus.

<script>

export default {

  methods: {

    activeLocale() {

      return this.$i18n.locale

    }

  }

}

</script>



<template>

  <!-- Mal angenommen, die aktive Sprache ist en-US -->

  <p>{{ $i18n.locale}}</p> <!-- => <p>en-US</p> -->



  <p>{{ activeLocale() }}</p> <!-- => <p>en-US</p> -->

</template>



Wir können auch einen neuen Wert für $i18n.locale festlegen, um eine neue aktive Locale zu setzen. Wir sehen das gleich in Aktion.

Refactoring der i18n-Bibliothek

In den nächsten Abschnitten gehen wir einige fortgeschrittene Themen wie lokalisierte Routen und asynchrones Laden von Übersetzungsdateien an. Das wird einfacher umzusetzen sein, wenn wir unsere kleine i18n-Bibliothek umgestalten, damit wir steuern können, wie Locales festgelegt und geladen werden.

importiere { createI18n } von 'vue-i18n'

import { messages } from './messages'

import { numberFormats } from './numbers'

import { arabischePluralRegeln } von './plurals'

import { datetimeFormats } von './datetimes'



// Setze und lege die Standard-Locale fest

export const defaultLocale = 'en-US'



// Private Instanz des VueI18n-Objekts

let _i18n



// Initializer

function setup(options = { locale: defaultLocale }) {

  _i18n = createI18n({

    locale: options.locale,

    fallbackLocale: defaultLocale,

    messages,

    numberFormats,

    datetimeFormats,

    pluralizationRules: {

      'ar-EG': arabischePluralRegeln,

    },

  })



  setLocale(options.locale)



  return _i18n

}



// Setzt die aktive Sprache. 

function setLocale(newLocale) {

  _i18n.global.locale = newLocale

}



// Öffentliche Schnittstelle

export default {

  // Stelle die VueI18n-Instanz über einen Getter bereit

  get vueI18n() {

    return _i18n

  },

  setup,

  setLocale,

}



🗒️ Hinweis » Vue I18n unterstützt Scoping, das wir verwenden können, um die Locale für einen Teil der Komponentenhierarchie unserer App zu ändern. Wir verwenden i18n.global, um auf den globalen, appweiten Scope von Vue I18n zuzugreifen. Das ist der Standard-Scope von Vue I18n, und wir arbeiten in diesem Artikel nur mit dem globalen Scope.

Schauen wir mal, wie sich der Rest unserer App durch diese Überarbeitung verändert.

import { createApp } from 'vue'

import App from './App.vue'

import router from './router'

import i18n from './i18n'

import './assets/main.css'



const app = createApp(App)



// Initialisiere die i18n-Bibliothek ausdrücklich

i18n.setup()

// Gib die VueI18n-Instanz als Plugin an use() weiter

app.use(i18n.vueI18n)

app.use(router)



app.mount('#app') 



Nichts anderes in unserer App muss geändert werden, doch durch unser Refactoring können wir in den folgenden Abschnitten leichter komplexere Features entwickeln.

Wie kann ich meine Routen lokalisieren?

Es ist oft eine gute Idee, darauf zu achten, dass unsere URLs den dazugehörigen Inhalt widerspiegeln. Lokalisierte URLs können bedeuten, dass /en-US/foo und /ar-EG/foo auf die englischen und arabischen Versionen der foo-Seite verweisen. Lass uns das in unserer Demo-App zum Laufen kriegen.

Als Erstes gucken wir mal, wie wir die Routen in unserer Demo eingestellt haben.

import { createRouter, createWebHistory } from 'vue-router'

import HomeView from '../views/HomeView.vue'



const router = createRouter({

  history: createWebHistory(import.meta.env.BASE_URL),

  routes: [

    {

      path: '/',

      name: 'home',

      component: HomeView

    },

    {

      path: '/about',

      name: 'about',

      // Lazy-loaded via Code-Splitting

      component: () => import('../views/AboutView.vue')

    }

  ]

})



export default router



Unser ziemlich einfaches Setup lädt die / Route unser <HomeView> und /about lädt unser <AboutView>. Die Komponenten werden in einem <router-view> in der Hauptkomponente <App> geladen.

<script setup>

importiere { RouterView } aus 'vue-router'

importiere Nav aus './components/Nav.vue'

importiere Footer aus './components/Footer.vue'

</script>



<template>

  <div>

    <header>

      <Nav />

    </header>



    <RouterView />



    <Footer />

  </div>

</template>



Lass uns diese Routen lokalisieren, damit /en-US/about die englische About-Seite zeigt und /ar-EG/about die arabische About-Seite zeigt.

import { createRouter, createWebHistory } from 'vue-router'

import { defaultLocale } from '../i18n'

import HomeView from '../views/HomeView.vue'



const router = createRouter({

  history: createWebHistory(import.meta.env.BASE_URL),

  routes: [

    // Der Wurzelpfad leitet immer zu einer

    // lokalisierten Route

    {

      path: '/',

      redirect: `/${defaultLocale}`,

    },

    // Alle Pfade unter dem Wurzelpfad sind lokalisiert

    {

      path: '/:locale',

      children: [

        {

          // Der leere Pfad legt die Standard-

          // Kind-Routenkomponente

          path: '',

          component: HomeView,

        },

        {

          // Nutzung des relativen 'about', nicht des absoluten

          // '/about' erlaubt uns, das :locale einzubinden

          // Parameter von der Elternkomponente.

          path: 'about',

          component: () => import('../views/AboutView.vue'),

        },

      ],

    },

  ],

})



exportiere standardmäßig den Router



🔗 Ressource » Der offizielle Vue Router Leitfaden ist ein super Startpunkt, um die Basics vom Vue-Routing zu lernen.

Mit diesen Änderungen, wenn wir jetzt / besuchen, landen wir bei /en-US (angenommen, en-US ist unser eingestelltes Standard-Locale). /en-US ist unsere lokalisierte Haupt-Route. Es zeigt standardmäßig das <HomeView> in der <App>’s <router-view>. /en-US/about zeigt das <AboutView>.

Wenn wir allerdings /ar-EG oder eine andere arabische Route besuchen, begrüßen uns englische Übersetzungen. Das liegt daran, dass wir das aktive Locale nicht wechseln, wenn sich der :locale Routenparameter ändert. Lass uns das mit einem beforeEach Router-Navigationsschutz beheben.

import { createRouter, createWebHistory } from 'vue-router'

import i18n, { defaultLocale } from '../i18n'

// ...



const router = createRouter({

  // ...

})



router.beforeEach((to, from) => {

  const newLocale = to.params.locale

  const prevLocale = from.params.locale



  // Wenn sich das Locale nicht geändert hat, mach nichts

  if (newLocale === prevLocale) {

    return

  }



  i18n.setLocale(newLocale)

})



exportiere standardmäßigen Router



Der praktische globale beforeEach() Guard des Vue-Routers läuft vor jeder Navigation und sorgt dafür, dass wir es mitbekommen, wenn sich der Locale-Parameter in der URL ändert. Wir geben einen anonymen Callback an den Guard weiter und nutzen unsere neue setLocale() Funktion, um die aktive Locale von Vue I18n zu aktualisieren, wenn sich der Locale-Parameter ändert. Das heißt, wenn wir /ar-EG/about aufrufen, sehen wir die arabische Version der About-Seite.

Unsere arabischen Routen zeigen jetzt arabische Übersetzungen an

Unsere arabischen Routen zeigen jetzt arabische Übersetzungen an

Wie erstelle ich eine wiederverwendbare lokalisierte Link-Komponente?

Ein Problem mit unserer aktuellen lokalisierten Routing-Lösung ist, dass wir den :locale Routenparameter jedes Mal manuell einfügen müssen, wenn wir einen Router-Link erstellen.

<script setup>

importiere { RouterLink } aus 'vue-router'

</script>



<template>

  <nav>

    <!-- ... -->

    <router-link :to="`/${$i18n.locale}`">

      {{ $t('home') }}

    </router-link>



    <router-link :to="`/${$i18n.locale}/about`">

      {{ $t('about') }}

    </router-link>

    </nav>

</template>



Das lässt sich nicht besonders gut skalieren und ist fehleranfällig. Lass uns das DRY (Don’t Repeat Yourself) machen, indem wir Vues <router-link> in eine benutzerdefinierte Komponente packen, die die Routenlokalisierung automatisch übernimmt.

<script>

importiere { RouterLink } aus 'vue-router'



export default {

  // Zeige die to-Eigenschaft, um relative Pfade zu akzeptieren,

  // nicht-lokalisierte URIs

  props: ['to'],



  components: { RouterLink },



  computed: {

    localizedUrl() {

      // Die Wurzel / Route ist speziell, weil sie

      // absolute

      return this.to === '/'

        ? `/${this.$i18n.locale}`

        : `/${this.$i18n.locale}/${this.to}`

    },

  },

}

</script>



<template>

  <!-- Intern nutzen wir einfach Vue's 

       guter alter Router-Link -->

  <router-link :to="localizedUrl">

    <slot></slot>

  </router-link>

</template>



Unser neuer <LocalizedLink> ist fast ein direkter Ersatz für <router-link>s. Wir müssen nur darauf achten, relative URLs für alles außer der Root-Route zu verwenden.

<script setup>

import { RouterLink } aus 'vue-router'

import LocalizedLink from './l10n/LocalizedLink.vue'

</script>



<template>

  <nav>

    <!-- ... -->



    <LocalizedLink to="/">

      {{ $t('home') }}

    </LocalizedLink>

    <!-- Wenn die aktive Locale ar-EG ist, wird zu

         /ar-EG -->



    <!-- Beachte, dass wir auf about zeigen, nicht auf /about -->

    <LocalizedLink to="about">

      {{ $t('about') }}

    </LocalizedLink>

    <!-- Wenn die aktive Locale ar-EG ist, wird zu

         /ar-EG/about -->

  </nav>

</template>



Wie baue ich eine Sprachumschalter-Oberfläche?

Um unseren Seitenbesuchern die Möglichkeit zu geben, ihre Locales auszuwählen, lass uns eine Dropdown-Komponente für den Sprachwechsel erstellen, die unsere lokalisierten Routen verwendet. Zuerst konfigurieren und veröffentlichen wir die unterstützten Locales unserer App in unserer i18n-Bibliothek.

// ...



// Nutzung einer { localeCode: localeData } Struktur

// ermöglicht es uns, Metadaten wie einen Namen zu jedem hinzuzufügen

// Locale hinzufügen, während unsere Bedürfnisse wachsen.

export const supportedLocales = {

  'en-US': { name: 'English' },

  'ar-EG': { name: 'العربية (Arabic)' },

}



// ...



Wir können jetzt unsere supportedLocales importieren und sie in einer neuen <LocaleSwitcher>-Komponente verwenden, die ein einfaches <select> umschließt.

<script>

import { supportedLocales } from '../../i18n'



export default {

  methods: {

    // Wird aufgerufen, wenn der Nutzer eine neue Locale auswählt

    // aus dem Dropdown-Menü

    onLocaleChange(event_) {

      const newLocale = event_.target.value



      // Wenn die ausgewählte Locale dieselbe ist wie die

      // aktiv, nichts tun

      if (newLocale === this.$i18n.locale) {

        return

      }



      // Navigiere zur lokalisierten Stammroute für

      // die gewählte Locale

      this.$router.push(`/${newLocale}`)

    },

  },

  computed: {

    // Wandle unser supportedLocales-Objekt um in 

    // eine Liste von [{ code: 'en-US', name: 'Englisch' }, ...]

    locales() {

      return Object.keys(supportedLocales).map((code) => ({

        code,

        name: supportedLocales[code].name,

      }))

    },

  },

}

</script>



<template>

  <select

    :value="$i18n.locale"

    @change="onLocaleChange($event)"

  >

    <option 

      v-for="locale in locales"

      :key="locale.code"

      :value="locale.code"

    >

      {{ locale.name }}

    </option>

  </select>

</template>



🤿 Abtauchen » Wir nutzen $router.push() in unserem <LocaleSwitcher>, um zur Hauptseite der ausgewählten Sprache zu navigieren. Lerne mehr über die programmgesteuerte Navigation von Vue Router im offiziellen Guide.

Jetzt können wir unsere neue Komponente in die Navigationsleiste unserer App für unsere User einbauen.

<script setup>

import LocalizedLink from './l10n/LocalizedLink.vue'

import LocaleSwitcher from './l10n/LocaleSwitcher.vue'

</script>



<template>

  <div>

    <nav>

      <img :alt="$t('logo')" src="@/assets/logo.svg"/>

      <span class="font-bold text-purple-300">{{ $t('appTitle') }}</span>

      <LocalizedLink to="/">{{ $t('home') }}</LocalizedLink>

      <LocalizedLink to="about">{{ $t('about') }}</LocalizedLink>

    </nav>



    <LocaleSwitcher />

  </div>

</template>



Unser Sprachumschalter-Teil in Aktion

Unser Sprachumschalter-Teil in Aktion

Direkt an i18n.locale binden

Wenn du keine lokalisierten Routen verwendest, kannst du direkt an $i18n.locale wie folgt binden.

<script>

import { supportedLocales } from '../../i18n'



export default {

  computed: {

    locales() {

      // ...

    },

  },

}

</script>



<template>

  <!-- Verwendung von Vues v-model für die bidirektionale Bindung -->

  <select v-model="$i18n.locale">

    <option

      v-for="locale in locales"

      :key="locale.code"

      :value="locale.code"

    >

      {{ locale.name }}

    </option>

  </select>

</template>



🤿 Gehe tiefer » Du kannst mehr über Ändern der Locale in der Dokumentation von Vue I18n erfahren.

Wie lade ich meine Übersetzungsdateien asynchron?

Wenn unsere Apps wachsen und wir mehr unterstützte Locales hinzufügen, riskieren wir, unser Hauptpaket mit all unseren Übersetzungsnachrichten aufzublähen. Realistisch gesehen brauchen wir nur Nachrichten für die gewählte Locale des aktuellen Besuchers. Wir können unser Hauptpaket schlanker machen, indem wir nur die Nachrichten der aktiven Locale bei Bedarf herunterladen.

Lass uns dieses asynchrone Übersetzungs-Laden zu unserer Demo-App hinzufügen. Wir fangen damit an, unsere messages.js Datei in JSON-Dateien pro Locale aufzuteilen.

{

  "appTitle": "Mushahed",

  "Startseite": Home

  Über Über uns

  // ...

}



{

  "appTitle": "مشاهد",

  "home": "الرئيسية",

  "about": "نبذة عنا",

  // ...

}



Als nächstes fügen wir eine Ladefunktion zu unserer i18n-Bibliothek hinzu.

import { nextTick } from 'vue'



// ...



let _i18n



// ...



async function loadMessagesFor(locale) {

  const messages = await import(

    /* webpackChunkName: "locale-[request]" */ `../translations/${locale}.json`

  )



  _i18n.global.setLocaleMessage(locale, messages.default)



  return nextTick()

}



export default {

  get vueI18n() {

    return _i18n

  },

  setup,

  setLocale,

  loadMessagesFor,

}



loadMessagesFor() nutzt Webpacks asynchrone Code-Splitting und dynamische Importe, um die Übersetzungsdatei für die angegebene Locale asynchron zu laden. Sobald die Übersetzungsdatei geladen ist, werden die Nachrichten der Datei an Vue I18n übergeben und mit der angegebenen Sprache verknüpft. Um sicherzugehen, dass Vue das DOM schon aktualisiert hat, bevor wir es auflösen, geben wir das Promise von nextTick() zurück.

Jetzt können wir die beforeEach() Navigationsschutzfunktion in unserem Router updaten, um die Nachrichten der Spracheinstellung zu laden, bevor wir die zugehörige Komponente einer Route rendern.

import { createRouter, createWebHistory } from 'vue-router'

import i18n, { defaultLocale } from '../i18n'



// ...



const router = createRouter({

  // ...

})



// Wir machen die Callback-Funktion async...

router.beforeEach(async (to, from) => {

  const newLocale = to.params.locale

  const prevLocale = from.params.locale



  if (newLocale === prevLocale) {

    return

  }



  // ...damit wir auf das Laden der Nachrichten warten können

  // bevor wir weitermachen

  await i18n.loadMessagesFor(newLocale)



  i18n.setLocale(newLocale)

})



export default router



Wenn wir unsere App jetzt neu laden, sollten wir keine großen Änderungen sehen. Aber wenn wir den Netzwerk-Tab in unseren Browser-Entwicklungstools öffnen, sollten wir sehen, dass eine Nachrichten-JSON-Datei geladen wird, wenn wir die Spracheinstellungen ändern.

Mit Webpacks Code-Splitting die Übersetzungen einer Spracheinstellung asynchron laden

Mit Webpacks Code-Splitting die Übersetzungen einer Spracheinstellung asynchron laden

🔗 Ressource » Erfahre mehr über asynchrones/faules Laden im Leitfaden zum Lazy Loading von Vue I18n.

Wie gehe ich mit Spracheinstellungs-Fallbacks um?

Vielleicht hast du einige Konsolenwarnungen bemerkt, nachdem du das asynchrone Laden von Übersetzungen oben eingeführt hast. Die Warnungen treten auf, wenn du eine en-US Route zum ersten Mal aufrufst.

Vue I18n versucht, auf eine allgemeinere Locale zurückzugreifen, wenn es keine Übersetzungsnachricht finden kann

Vue I18n versucht, auf eine allgemeinere Locale zurückzugreifen, wenn es keine Übersetzungsnachricht finden kann

Was passiert, ist, dass Vue I18n keine en-US Nachricht findet, wenn die App zum ersten Mal geladen wird. Die en-US Nachrichten werden in einer HTTP-Anfrage geladen, die vom Hauptbundle getrennt ist, sodass sie möglicherweise nicht verfügbar sind, wenn die App zum ersten Mal geladen wird. Wir kümmern uns gleich darum.

Beachte jedoch, dass Vue I18n versucht, die logo Nachricht in einer allgemeinen en Locale zu finden, wenn sie sie nicht in der regionsspezifischen en-US Locale finden kann. Das ist das Standardverhalten der Bibliothek für Fallback. Das kann ziemlich praktisch sein, wenn bei einer unserer Locales Übersetzungen fehlen.

🤿 Gehe tiefer » Check mal alle Optionen, die Vue I18n für den Fallback im Fallback-Guide bietet.

Eine fallbackLocale Konfigurationsoption haben wir als allgemeine Auffangoption: Alle Locales, die wir unter fallbackLocale auflisten, werden genutzt, um eine Nachricht anzuzeigen, wenn sonst keine gefunden werden kann. Nutzen wir diese Option, um sicherzugehen, dass wir in unserer App auf Englisch zurückgreifen.

// ...

import { createI18n } from 'vue-i18n'

import { numberFormats } from './numbers'

import { arabicPluralRules } from './plurals'

import { datetimeFormats } from './datetimes'

import defaultMessages from '../translations/en-US.json'



export const defaultLocale = 'en-US'



let _i18n



function setup(optionen = { locale: defaultLocale }) {

  _i18n = createI18n({

    locale: options.locale,

    fallbackLocale: defaultLocale,

    messages: { [defaultLocale]: defaultMessages },

    numberFormats,

    datetimeFormats,

    pluralizationRules: {

      'ar-EG': arabicPluralRules,

    },

  })



  setLocale(options.locale)



  return _i18n

}



// ...



Wir importieren unsere en-US Übersetzungsnachrichten und geben sie in die messages Option von Vue I18n. Dies wird unsere englischen Nachrichten im Hauptpaket enthalten, sodass unsere App nicht warten muss, bis sie asynchron geladen werden. Wenn en-US als fallbackLocale eingestellt ist, wird die entsprechende englische Nachricht angezeigt, anstatt dass eine Nachricht in einer anderen Sprache fehlt.

Die englische Nachricht für "Home" wird anstelle der fehlenden arabischen Nachricht verwendet

Die englische Nachricht für "Home" wird anstelle der fehlenden arabischen Nachricht verwendet

Handliche Konsolenwarnungen zeigen die Fallback-Kette von Vue I18n

Handliche Konsolenwarnungen zeigen die Fallback-Kette von Vue I18n

Damit ist unsere kleine Demo-App internationalisiert.

Unsere internationalisierte Demo-App

Unsere internationalisierte Demo-App

🔗 Ressource Du kannst den gesamten Code der internationalisierten Options-API-Demo-App, die wir oben erstellt haben, von GitHub abrufen. Der Democode enthält einige Funktionen, für die wir in diesem Artikel keinen Platz hatten, wie das Lauschen auf Locale-Änderungen, um Daten neu zu laden, und Unterstützung für Sprachen von rechts nach links.

Wie kann ich meine Vue-App mit der Composition API lokalisieren?

Alles, was wir bisher in diesem Artikel behandelt haben, bezieht sich auf Vues objektorientierte Options API. Wenn deine App die Composition API in Vue 3 verwendet, bist du in diesem Abschnitt gut aufgehoben.

Hinweis » Vue I18n ist so konzipiert, dass es mit entweder der Options API oder der Composition API funktioniert, aber nicht mit beiden gleichzeitig. Die Vue I18n Options API ist die Standard-API und wird als Legacy API genannt. Lies den Leitfaden zur Migration von der Legacy API zur Composition API, um mehr über Einschränkungen und Vorbehalte zu erfahren.

Bevor wir unseren I18n-Code umstrukturieren, schauen wir uns kurz an, wie wir unsere Vue-Komponenten (ohne I18n) von der Options API zur Composition API umstrukturieren.

🗒️ Hinweis » Die folgenden Abschnitte bauen auf dem auf, was wir schon in diesem Artikel besprochen haben. Wenn du neu bei Vue I18n bist, wird empfohlen, den Rest des Artikels zu lesen, bevor du fortfährst.

Nur drei Dateien in unserer Demo müssen für die Composition umstrukturiert werden: AstroCard.vue, Astronauts.vue und Coords.vue.

<-- Nutze den syntaktischen Zucker für das Skript-Setup für Einzeldateikomponenten -->

<script setup>



// Funktionen von Vue importieren

import { computed } from 'vue'



// Makro verwenden, um `props` zu definieren 

const props = defineProps({

  name: String

  photoUrl: String

  nationalität: String

  handwerk: String

})



// erstelle eine berechnete eigenschaft mit computed()

// und refaktoriere die prop-referenz, um `props.X` zu verwenden

const fullPhotoUrl = computed(() => `/img/astros/${props.photoUrl}`)

</script>



<template>

  <!-- Im Template ändert sich nichts. -->

</template>



<script setup>

import { ref } from 'vue'

import AstroCard from './AstroCard.vue'



// verwende ref, um reaktive daten zu definieren

const loading = ref(true)

const astros = ref([])



// Logik, die in created() ausgeführt werden würde

// direkt auf der obersten Ebene geschrieben

fetch('/data/astronauts.json')

  .then((res) => res.json())

  .then((data) => {

    // Denk daran, .value zu verwenden, wenn du abrufst/einstellst

    // Werte, die mit ref() definiert sind

    astros.value = data

    loading.value = false

  })

</script>



<template>

  <!-- Im Template ändert sich nichts. -->

</template>



🔗 Ressource » Wir hören hier auf, um es kurz zu halten und zum i18n zu kommen. Du kannst die Unterschiede für die Refaktorisierung der Composition API (vor i18n) in unserem GitHub-Repo ansehen.

Refactoring von i18n zur Nutzung der Composition API

Es gibt nicht zu viel zu ändern, wenn wir die Composition API von Vue I18n nutzen wollen. Hier ist, was wir abdecken werden:

  • Stelle legacy: false ein, wenn du die VueI18n-Instanz erstellst.
  • Refactoring von vueI18n.global.locale zu vueI18n.global.locale.value.
  • Refactoring von tc() Aufrufen zu t() für Plurale.
  • Refactoring aller this.X Aufrufe zu ihren funktionalen Entsprechungen in unseren Komponenten-<script>s.

Lass uns loslegen.

Legacy-Modus ausschalten

Standardmäßig befindet sich Vue I18n im "Legacy"-Modus, in dem createI18n() eine VueI8n-Objektinstanz zurückgibt. Wir wollen, dass die Fabrikfunktion eine Composer Instanz erstellt, die Funktionen wie t() und n() für unsere Kompositionskomponente bereitstellt.

Um das hinzukriegen, müssen wir nur eine Option an createI18n() übergeben und legacy: false setzen.

// ...

import { createI18n } from 'vue-i18n'



export const defaultLocale = 'en-US'



// ...



let _i18n



function setup(Optionen = { locale: defaultLocale }) {

  _i18n = createI18n({

    legacy: false,

    // Nichts anderes ändert sich in unseren Optionen

  })



  setLocale(options.locale)



  return _i18n

}



// ... 



Reaktive Eigenschaften verwenden

Sobald wir die Composition mit legacy: false nutzen, müssen wir unsere Aufrufe an Vue I18n’s locale umstellen, weil locale jetzt wie ein reaktives Ref funktioniert.

// ...



function setLocale(newLocale) {

  // Genau wie bei jedem reaktiven Vue-Ref, haben wir

  // es mit .value zu setzen/holen

  _i18n.global.locale.value = newLocale

  setDocumentAttributesFor(newLocale)

}



// ...



Das ist alles, was wir in unserer i18n-Bibliothek ändern müssen. Die restlichen Updates finden in unseren Komponenten statt.

🔗 Ressource » Schau dir den Composition API Guide für mehr Infos an.

Nutze t() statt tc() für Nachrichten im Plural

Die Composer-Instanz von Vue I18n hat keine tc() Funktion zum Ausgeben von Nachrichten im Plural; wenn wir zur Composition API wechseln, gibt Vue I18n immer dann einen Fehler aus, wenn wir versuchen, tc() zu nutzen. Stattdessen können wir einfach die normale t() Funktion als direkten Ersatz nutzen.

<script setup>

  // ...

</script>



<template>

  <!-- ... -->



  <div>

    <div>

      <h2>

      <!-- Verwende $t() anstelle von $tc(): funktioniert genau gleich -->

        🧑‍🚀 {{ $t('peopleInSpace', astros.length) }}

      </h2>



      <p>

        {{ $t('updatedAt', { date: $d(updated, 'short') }) }}

      </p>

    </div>



    <!-- ... -->

    </div>

  </div>

</template>



🗒️ Hinweis » $t(), $d() und $n() klappen in Komponenten <template>s sowohl im Legacy- als auch im Composition-Modus. Das liegt daran, dass Vue I18n standardmäßig sie global in beiden Modi injiziert. Das ist nicht der Fall bei der Komponente <scripts>, wo $t(), $d() usw. im Composition-Modus nicht verfügbar sind. Damit beschäftigen wir uns als Nächstes.

Lokalisierungsfunktionen in Komponentenskripten nutzen

In unserer Coords-Komponente nutzen wir this.$t(), this.$d() und this.$n(), um Übersetzungsnachrichten sowie lokalisierte Daten und Zahlen abzurufen.

<script>

export default {

  data() {

    return {

      loading: true,

      coords: null,

      datetime: '',

    }

  },



  created() {

    // Wir ziehen hier Koordinatendaten aus dem Netzwerk.

  },



  computed: {

    issPosition() {

      const { latitude, longitude } = this.coords

     

      return this.$t('issPosition', {

        latitude: this.$n(latitude, 'coords'),

        longitude: this.$n(longitude, 'coords'),

        datetime: this.$d(this.datetime, 'full'),

      })

    },

  },

}

</script>



<template>

  <!-- Koordinatendaten anzeigen -->

</template>



Wenn wir zur Composition API wechseln, haben wir this nicht mehr, das auf die Komponenteninstanz zeigt, also können wir this.$t() und ähnliche Funktionen nicht mehr nutzen. Vue I18n stellt eine useI18n()-Funktion bereit, die die Composer-Instanz zurückgibt, in der t() und Co. enthalten sind. Schauen wir uns das mal in Aktion an.

<script setup>

import { ref, computed } from 'vue'

import { useI18n } from 'vue-i18n'



// Funktionen aus der zurückgegebenen Composer-Instanz herausnehmen

const { t, n, d } = useI18n()



const loading = ref(true)

const coords = ref(null)

const datetime = ref('')



// Wir holen hier Koordinatendaten aus dem Netzwerk...



const issPosition = computed(() => {

  const { latitude, longitude } = coords.value



  // Funktionen ohne `this` nutzen

  return t('issPosition', {

    latitude: n(latitude, 'coords'),

    longitude: n(longitude, 'coords'),

    datetime: d(datetime.value, 'full'),

  })

})

</script>



<template>

  <!-- ... -->

</template>



Heads up » Benutze nicht das $-Präfix mit den funktionalen Varianten in den <script>s Komponenten.

Nutze die reaktive locale-Eigenschaft

Genau wie this.$t() überarbeitet werden musste, muss auch this.$i18n.locale überarbeitet werden. Wir können die aktive Locale holen und festlegen, indem wir sie aus der Composer-Instanz in unseren Komponenten destrukturieren. Lass uns unsere LocaleSwitcher- und LocalizedLink-Komponenten überarbeiten, um die reaktive locale-Eigenschaft zu nutzen.

<script setup>

import { computed } from 'vue'

import { useI18n } from 'vue-i18n'

import { useRouter } from 'vue-router'

import { supportedLocales } from '../../i18n'



const router = useRouter()

const { locale } = useI18n()



function onLocaleChange(event_) {

  const newLocale = event_.target.value



  // Genau wie andere reaktive Referenzen, müssen wir 

  // locale.value verwenden, um die aktive Locale zu holen/setzen

  if (newLocale === locale.value) {

    return

  }



  router.push(`/${newLocale}`)

}



const locales = computed(() =>

  Object.keys(supportedLocales).map((code) => ({

    Code,

    name: supportedLocales[code].name,

  }))

)

</script>



<template>

  <!-- Beachte, dass $i18n.locale immer noch verfügbar ist in

       unseren Komponenten-Templates -->

  <select

    :value="$i18n.locale"

    @change="onLocaleChange($event)"

  >

    <option v-for="locale in locales" :key="locale.code" :value="locale.code">

      {{ locale.name }}

    </option>

  </select>

</template>



<script setup>

import { computed } from 'vue'

import { RouterLink } from 'vue-router'

import { useI18n } from 'vue-i18n'



const props = defineProps(['to'])



const { locale } = useI18n()



const localizedUrl = computed(() =>

  props.to === '/' ? `/${locale.value}` : `/${locale.value}/${props.to}`

)

</script>



<template>

  <router-link :to="localizedUrl">

    <slot></slot>

  </router-link>

</template>



🔗 Ressource » Die Composer-Instanz zeigt weitere Eigenschaften: Check mal die API-Dokumentation für eine komplette Liste aus.

Und damit haben wir das Refactoring abgeschlossen.

Unsere internationalisierte Demo-App

Unsere Demo läuft genau wie im Legacy-Modus

🔗 Ressource » Du kannst den kompletten Code für die Composition i18n-Demo von GitHub abrufen.

🔗 Ressource » Wenn du an allgemeinem JavaScript i18n interessiert bist, einschließlich anderer UI- und i18n-Bibliotheken, könnte dir unser Ultimativer Leitfaden zur JavaScript-Lokalisierung gefallen.

Das war's dann mit unserer Vue 3 i18n-Demo. Wir hoffen, es hat dir gefallen und du hast ein paar Dinge auf dem Weg gelernt. Wenn du dein i18n-Spiel auf die nächste Stufe heben möchtest, schau dir Phrase an. Phrase unterstützt Vue I18n direkt mit seinem In-Context Editor, der es deinen Übersetzern ermöglicht, Nachrichten direkt in deiner App zu aktualisieren. Die voll ausgestattete Phrase-Webkonsole mit maschinellem Lernen und intelligenten Vorschlägen ist eine Freude für Übersetzer:innen. Sobald die Übersetzungen bereit sind, können sie automatisch mit deinem Projekt synchronisiert werden – Phrase kommt mit einer CLI und synchronisiert mit Bitbucket, GitHub und GitLab. Du stellst es ein und vergisst es, sodass du dich auf den Code konzentrieren kannst, den du liebst. Check mal alle Funktionen aus, die Phrase zu bieten hat, und gib ihm eine Drehung mit einer 14-tägigen kostenlosen Testversion.