Software-Lokalisierung
Ein umfassender Leitfaden zur Vue-Lokalisierung

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
Attributionen
Ein fettes Dankeschön an die folgenden Leute und Organisationen, die ihre Sachen kostenlos rausgehauen haben.
- Wir haben das Satellit-Symbol von Akriti Bhusal auf The Noun Project für unser Demo-Brand-Icon genutzt.
- Das Foto von Cai Xuzhe und das Foto von Chen Dong sind urheberrechtlich geschützt vom China News Service und werden unter der CC BY 3.0 Lizenz genutzt.
- Das Foto von Liu Yang ist urheberrechtlich geschützt von Manfred Werner (Tsui) und wird unter der CC BY-SA 3.0 Lizenz genutzt.
- Alle anderen Astronautenfotos sind Public Domain.
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

🗒 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" }, // ... ]

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
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:
- In unserem
messages
-Objekt fügen wir unter jedem unserer Locales Übersetzungen mit einem gemeinsamen Schlüssel hinzu. - 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 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>
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 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
<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 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.
✋ 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.
🔗 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
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
🔗 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
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
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
🔗 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
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
Handliche Konsolenwarnungen zeigen die Fallback-Kette von Vue I18n
Damit ist unsere kleine Demo-App internationalisiert.
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
zuvueI18n.global.locale.value
. - Refactoring von
tc()
Aufrufen zut()
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 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.