Nemzetköziesítés a @angular/localize segítségével

Nagy előrelépés történt az i18n fronton!Az Angular 9.0.

Ez a motorháztető alatt ugyanazokat a funkciókat biztosítja, mint korábban:fordítások a sablonokban fordítási időben.

De többet enged remélni a jövőben,a már elérhető nem dokumentált funkciókkal,mint a fordítások a kódban,vagy a futásidejű fordítások a csak fordítási fordítások helyett 😎.

Kezdjük azzal,hogy megnézzük mit tehetünk a @angular/localizeés a CLI segítségével a v9-ben.

i18n a sablonokban

Az új @angular/localize csomag egy $localize nevű funkciót kínál.

Az Angularban meglévő i18n támogatás mostantól a $localize-t használja,ami azt jelenti, hogy az olyan sablonok, mint:

<h1 i18n>Hello</h1>

a $localize hívásokra lesznek fordítva.

Ha egy ilyen sablonnal futtatjuk a ng serve-t,futásidejű hibába ütközünk:

Error: It looks like your application or one of its dependencies is using i18n.Angular 9 introduced a global `$localize()` function that needs to be loaded.Please run `ng add @angular/localize` from the Angular CLI.(For non-CLI projects, add `import '@angular/localize/init';` to your `polyfills.ts` file.For server-side rendering applications add the import to your `main.server.ts` file.)

A hiba magától értetődő:mivel a i18n attribútumok most $localize hívássá alakulnak át a generált kódban,be kell töltenünk a $localize függvényt.Ezt megtehetnénk alapértelmezetten,de mivel csak akkor van rá szükség,ha nemzetköziesítést használunk,nem igazán lenne értelme.

Ezért ha az alkalmazásunk,vagy annak valamelyik függősége i18n attribútumokat használ a sablonjaiban,akkor a import '@angular/localize/init'-öt hozzá kell adnunk a polyfilljeinkhez!

A CLI kínál egy sémát,hogy ezt megtegyük helyettünk.Egyszerűen futtasd:

ng add @angular/localize

és a CLI hozzáadja a csomagot a függőségeidhez és a szükséges importot a polikitöltéseidhez.

Aztán amikor futtatod az alkalmazásodat egy egyszerű ng serve-val,$localize egyszerűen megjeleníti az eredeti üzenetet.

Most hogyan fordítod ezeket az üzeneteket?

A folyamat nagyon hasonló a korábbiakhoz.Először lefuttatod a ng xi18n programot, hogy az üzeneteket egy messages.xlf fájlba kivonatold.Ezután lefordítod a fájlt a helyi nyelvek számára, például messages.fr.xlfés messages.es.xlf.

Ezt követően konfigurálnod kell a CLI-t, a angular.json

{ "projects": { "ponyracer": { "projectType": "application", // ... "i18n": { "locales": { "fr": "src/locale/messages.fr.xlf", "es": "src/locale/messages.es.xlf", } }, "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", // ... "configurations": { "production": { // ... }, "fr": { "localize": }, "es": { "localize": } } }, "serve": { // ... "configurations": { "production": { // ... }, "fr": { "browserTarget": "ponyracer:build:fr" }, "es": { "browserTarget": "ponyracer:build:es" } } } // ...}

Most a es vagy fr konfigurációk lehetővé teszik a futtatást:

ng serve --configuration=fr

És a kiszolgált alkalmazás már francia nyelven van!

Az alkalmazást egy adott lokalitással is építheted:

ng build --configuration=production,es

vagy az összes lokalitással egyszerre:

ng build --prod --localize

Ez nagy előrelépés a korábbi Angular verziókhoz képest.Korábban minden lokalitáshoz ugyanazt az alkalmazást kellett építenünk,mivel a fordítás része volt a fordításnak.Most, amikor az Angular lefordítja az alkalmazást, $localize hívásokat generál.Aztán, amikor ez megtörtént, egy eszköz fogja a lefordított alkalmazást és kicseréli az összes $localize hívást a megfelelő fordításokra.Ez szuper gyors.Ezután egy olyan csomagot kapsz, amely nem tartalmaz $localize hívásokat és minden i18n karakterlánc le lett fordítva.

Előtte minden nyelvterületre egyszer kellett lefordítani az alkalmazást, és ez egy teljes build volt. Tehát mondjuk egy 30s build,és 4 locales-t akartál,akkor 2 percig tartott.

Az új megközelítéssel a fordítás egyszer történik,és utána a különböző i18n verziók pár másodperc alatt generálódnak(ha lehet,akkor párhuzamosan is generálódnak).Tehát 2 percről ~40 másodpercre csökken! 🌈

Ezután több csomagod van, lokalitásonként egy,és a felhasználóknak a preferenciáiktól függően a megfelelőt tudod kiszolgálni, ahogy eddig is tetted.

Ezt a stratégiát fordítási idejű inliningnek hívják,mivel a fordításokat közvetlenül inline-olod,és így futásidőben már nincs mit tenni.

Most beszéljünk egy még nem dokumentált dologról,ami a jövőben változhat,de mégis érdekes tudni:most már a TypeScript kódunkban is tudunk üzeneteket fordítani!

A $localize függvényt,amiről már beszéltem,közvetlenül is használhatjuk. Ez egy sajátos függvény,amivel megjelölhetünk egy sablonsztringet a lokalizációhoz.

De talán azzal kellene kezdenünk,hogy elmagyarázzuk,mi is az a megjelölt sablonsztring?

Sablonsorok és tagfüggvények

Sablonsorok használatakor definiálhatunk egy tagfüggvényt,és alkalmazhatjuk egy sablonsztringre.Itt askQuestion hozzáad egy kérdőpontot a string végére:

const askQuestion = strings => strings + '?';const template = askQuestion`Is anyone here`;

Mi a különbség egy egyszerű függvényhez képest?A tagfüggvény valójában több argumentumot kap:

  • a karakterlánc statikus részeinek tömbjét
  • a kifejezések kiértékeléséből származó értékeket

Ha például van egy kifejezéseket tartalmazó sablonsztringünk:

const person1 = 'Cedric';const person2 = 'Agnes';const template = `Hello ${person1}! Where is ${person2}?`;

akkor a tagfüggvény megkapja a különböző statikus és dinamikus részeket.Itt van egy tag függvényünk a főszereplők nevének nagybetűs írására:

const uppercaseNames = (strings, ...values) => { // `strings` is an array with the static parts // `values` is an array with the evaluated expressions const names = values.map(name => name.toUpperCase()); // `names` now has // let's merge the `strings` and `names` arrays return strings.map((string, i) => `${string}${names ? names : ''}`).join('');};const result = uppercaseNames`Hello ${person1}! Where is ${person2}?`;// returns 'Hello CEDRIC! Where is AGNES?'

i18n a $localize a TypeScript kódban

$localize ezt a mechanikát használja, hogy leírhassuk:

@Component({ template: '{{ title }}'})export class HomeComponent { title = $localize`You have 10 users`;}

Megjegyezzük, hogy nem kell importálni a függvényt.Amíg a import '@angular/localize/init'-öt egyszer hozzáadod az alkalmazásodban,a $localize hozzáadódik a globális objektumhoz.

Az üzenetet ezután ugyanúgy lefordíthatod, mint egy sablon esetében.De jelenleg (v9.0.0) a CLI nem vonja ki ezeket az üzeneteket a xi18n paranccsal, mint a sablonok esetében.

Ha kiszolgálja az alkalmazást, és nem talál fordítást, a $localize egyszerűen megjeleníti az eredeti karakterláncot,és figyelmeztetést ír ki a konzolba:

No translation found for "6480943972743237078" ("You have 10 users").

Ezért kézzel kell hozzáadni a messages.fr.xlfhoz a megadott azonosítóval,ha ki akarod próbálni:

<trans-unit> <source>You have 10 users</source> <target>Vous avez 10 utilisateurs</target></trans-unit>

A HomeComponent sablonom sablonja ekkor megjeleníti a Vous avez 10 utilisateurs!

Mi történik, ha valamilyen dinamikus kifejezés van a sablonsztringben?

title = $localize`Hi ${this.name}! You have ${this.users.length} users.`;

A kifejezések automatikusan PH és PH_1 nevet kapnak (PH a helyőrzőnek van).Ezután ezeket a placeholdereket bárhol használhatja a fordításokban:

<trans-unit> <source>Hi <x/>! You have <x/> users.</source> <target>Bonjour <x/>&nbsp;! Vous avez <x/> utilisateurs.</target></trans-unit>

A legjobb gyakorlat azonban az, ha maga ad értelmes placeholdernevet a kifejezésnek,és ezt a ${expression}:placeholder: szintaxis használatával teheti meg.

title = $localize`Hi ${this.name}:name:! You have ${this.users.length}:userCount: users.`;

Aztán ezt a helyőrzőt bárhol használhatja a fordításokban:

<trans-unit> <source>Hi <x/>! You have <x/> users.</source> <target>Bonjour <x/>&nbsp;! Vous avez <x/> utilisateurs.</target></trans-unit>

Egyéni azonosítók

Figyeljen arra, hogy ha egyéni azonosítókkal rendelkező fordításai vannak, akkor azokat a $localize használja (ahogy korábban is volt):

<h1 i18n="@@home.greetings">Hello</h1>

Akkor a fordításod így néz ki:

<trans-unit> <source>Hello</source> <target>Bonjour</target></trans-unit>

ami nyilvánvalóan szebben használható.

Hogyan van a fordítások esetében a kódban?$localize is ért egy szintaxist, amely lehetővé teszi egy ID megadását:

title = $localize`:@@home.users:You have 10 users`;

Az egyéni ID szintaxisa ugyanaz, mint a sablonokban,és az ID-t kettőspontok veszik körül, hogy elválassza a fordítás tartalmától.

A sablonok szintaxisához hasonlóan megadhat egy leírást és egy jelentést is,hogy a fordítóknak segítsen egy kis kontextussal: :meaning|description@@id:message.

Például:

title = $localize`:greeting message with the number of users currently logged in@@home.users:You have 10 users`;

Ne feledjük, hogy ez egy alacsony szintű, dokumentálatlan API.Az Angular csapat vagy közösség valószínűleg magasabb szintű funkciókat fog kínálni jobb fejlesztői tapasztalattal (nos, remélem!).Locl Olivier Combe-tól,az ngx-translate szerzőjétőlvalószínűleg érdemes szemmel tartani 🧐.

Futásidejű fordítások

Amint említettem,ha a fenti CLI parancsokat használod (ng serve --configuration=fr vagy ng build --localize), akkor az alkalmazás lefordításra kerül, majd lefordításra, mielőtt a böngészőbe kerül, így nincsenek $localize hívások futásidőben.

De a $localize úgy lett kialakítva, hogy egy másik lehetőséget kínáljon:futásidejű fordításokat.Mit jelent ez? Nos, csak egy alkalmazást tudnánk szállítani,amely $localize hívásokat tartalmaz,és mielőtt az alkalmazás elindulna, betölthetnénk a kívánt fordításokat.Nincs többé N build és N bundle N locales-hez \o/

Nem merülve el túlságosan a részletekben,ez már lehetséges a v9-ben,a @angular/localize által kínált loadTranslations funkció használatával.De ezt az alkalmazás indítása előtt kell megtenni.

A fordításokat a polyfills.ts-be töltheted be:

import '@angular/localize/init';import { loadTranslations } from '@angular/localize';loadTranslations({ '1815172606781074132': 'Bonjour {$name}\xa0! Vous avez {$userCount} utilisateurs.'});

Amint látod, nincs locale figyelembevétel:a fordításodat egyszerűen objektumként töltöd be,amelynek kulcsai a lefordítandó karakterláncok,az értékei pedig azok fordításai.

Most ha futtatsz egy egyszerű ng serve-at,a cím francia nyelven jelenik meg!És nincs többé szükség ng xi18n-re,vagy messages.fr.xlfre,vagy a angular.json-ben az egyes locale-ok specifikus beállítására.Hosszú távon,amikor ez megfelelően támogatott és dokumentált lesz,képesnek kell lennünk JSON fájlokat betölteni futásidőben,ahogy a legtöbb i18n könyvtár teszi.Ezt még a v9-ben is el lehet érni, ez csak egy kis kézi munka,de megoldható.

Mi a helyzet a locale menet közbeni megváltoztatásával?Betölthetünk egy másik fordítási készletet, amikor az alkalmazás elindul?Nos, nem. A $localize hívások jelenlegi generálási módja lehetetlenné teszi, hogy utólag megváltoztassuk őket: újra kell indítani az alkalmazást.De ha nem bánod, hogy frissíted a böngészőt, lehetséges.Kipróbáltam egy egyszerű stratégiát, ami működik:

  • a felhasználó kiválaszt egy új nyelvet (pl. spanyol).
  • tároljuk a nyelvet a böngészőben (például a localStorage-ben)
  • újratöltjük az oldalt, ami újraindítja az alkalmazást
  • a polyfills.ts-ben a tárolt nyelv beolvasásával kezdjük
  • betöltjük a spanyolnak megfelelő fordításkészletet a loadTranslations segítségével.

Ez persze a jövőben gördülékenyebb lesz, vagy az Angular egy jövőbeli verziójában,vagy az ökoszisztéma egy könyvtárán keresztül.mindenesetre egyre közelebb vagyunk ahhoz, hogy csak egy verziót szállítsunk az alkalmazásunkból,és csak a fordításokat töltsük be futáskor \o/

Minden anyagunk (ebook, online képzés és tréning) naprakész ezekkel a változásokkal, ha többet szeretnél megtudni!

Leave a Reply