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.xlf
hoz 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/> ! 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/> ! 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.xlf
re,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