Internacionalizace pomocí @angular/localize

Na poli i18n došlo k velkému pokroku!V Angularu 9.0 byl zaveden nový balíček @angular/localize.

Pod kapotou nám poskytuje stejné funkce jako dříve:překlady v šablonách při kompilaci.

Dovoluje nám však doufat, že v budoucnu budeme mít k dispozici více nedokumentovaných funkcí,jako jsou překlady v kódu nebo překlady za běhu namísto překladů pouze při kompilaci 😎.

Začněme tím, že se podíváme, co můžeme dělat s pomocí @angular/localizea CLI ve verzi v9.

i18n v šablonách

Nový balíček @angular/localize nabízí funkci s názvem $localize.

Stávající podpora i18n v Angularu nyní používá $localize,což znamená, že šablony jako:

<h1 i18n>Hello</h1>

budou zkompilovány na volání $localize.

Pokud spustíte ng serve s takovou šablonou,narazíte na chybu při spuštění:

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.)

Chybu lze vysvětlit:protože atributy i18n jsou nyní ve vygenerovaném kódu převedeny na volání $localize,musíme načíst funkci $localize.Mohlo by to být provedeno ve výchozím nastavení,ale protože je to vyžadováno pouze v případě, že používáte internacionalizaci,nemělo by to skutečný smysl.

Proto pokud vaše aplikace nebo některá z jejích závislostí používá ve svých šablonách atributy i18n, pak budete muset do svých polyfillů přidat import '@angular/localize/init'!

CLI nabízí schéma, které to udělá za vás.Stačí spustit:

ng add @angular/localize

a CLI přidá balíček do vašich závislostí a potřebný import do vašich polyfillů.

Poté, když spustíte svou aplikaci s jednoduchým ng serve,$localize jednoduše zobrazí původní zprávu.

Teď jak tyto zprávy přeložíte?

Postup je velmi podobný tomu, který jsme měli dříve.Nejprve spustíte ng xi18n, abyste zprávy vyextrahovali do souboru messages.xlf. poté soubor přeložíte pro své lokality, například messages.fr.xlfa messages.es.xlf.

Poté musíte nakonfigurovat CLI, v 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" } } } // ...}

Nyní konfigurace es nebo fr umožní spustit:

ng serve --configuration=fr

A obsluhovaná aplikace je nyní ve francouzštině!

Můžete také sestavit aplikaci s konkrétním locale:

ng build --configuration=production,es

nebo se všemi locales najednou:

ng build --prod --localize

To je velký pokrok oproti předchozím verzím Angularu. dříve jsme museli sestavovat stejnou aplikaci pro každé locale, protože překlad byl součástí kompilace.Nyní Angular při kompilaci aplikace generuje volání $localize. když je to hotovo, nástroj vezme zkompilovanou aplikacia nahradí všechna volání $localize správnými překlady. to je superrychlé. pak máte balíček, který neobsahuje žádná volání $localizea všechny řetězce i18n byly přeloženy.

Dosud jste museli sestavit aplikaci jednou pro každé locale, a to bylo úplné sestavení. Takže řekněme, že to bylo 30s sestavení,a chtěli jste 4 lokality, pak jste byli na 2 minuty.

S novým přístupem se kompilace provede jednou,a pak se různé verze i18n vygenerují během několika sekund(dokonce se generují paralelně, pokud je to možné).takže se dostanete ze 2 minut na ~40 sekund! 🌈

Máte pak několik balíčků, jeden pro každou lokalizaci, a můžete uživatelům servírovat příslušný podle jejich preferencí, jak jste byli zvyklí.

Této strategii se říká compile-time inliningas, kdy překlady inlinujete přímo, a za běhu už pak není co řešit.

Teď si povíme o něčem zatím nedokumentovaném,co se možná v budoucnu změní,ale přesto je zajímavé to vědět:zprávy nyní můžeme překládat i v našem kódu TypeScriptu!

Funkci $localize, o které jsem mluvil, můžeme použít přímo. Je to zvláštní funkce, kterou můžete použít k označení šablonového řetězce pro lokalizaci.

Ale možná bychom měli začít tím, že si vysvětlíme, co je to označený šablonový řetězec?

Šablonové řetězce a značkovací funkce

Při použití šablonových řetězců můžete definovat značkovací funkci,a použít ji na šablonový řetězec.zde askQuestion přidá na konec řetězce dotazovací bod:

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

Takže jaký je rozdíl oproti jednoduché funkci?Funkce značky ve skutečnosti přijímá několik argumentů:

  • pole statických částí řetězce
  • hodnoty vyplývající z vyhodnocení výrazů

Pokud máme například šablonu řetězceobsahující výrazy:

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

tak funkce značky obdrží různé statické a dynamické části.Zde máme funkci tagu pro psaní velkých písmen ve jménech protagonistů:

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 s $localize v kódu TypeScript

$localize používá tuto mechaniku, abychom mohli napsat:

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

Všimněte si, že funkci nemusíte importovat.Pokud jednou přidáte import '@angular/localize/init' do své aplikace,$localize se přidá do globálního objektu.

Zprávu pak můžete přeložit stejným způsobem jako v případě šablony. v současné době (v9.0.0) však CLI tyto zprávy nevytahuje příkazem xi18n, jako je tomu u šablon.

Pokud aplikaci obsluhujete a nenajdete žádný překlad, $localize jednoduše zobrazí původní řetězec a do konzoly zaznamená varování:

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

Takže ho musíte ručně přidat do svého messages.fr.xlfs daným IDpokud to chcete zkusit:

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

Šablona mého HomeComponent pak zobrazí Vous avez 10 utilisateurs!

Co se stane, když máte v řetězci šablony nějaký dynamický výraz?

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

Výrazy se automaticky pojmenují PH a PH_1 (PH je pro zástupný symbol).Tyto zástupné znaky pak můžete použít kdekoli v překladech:

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

Nejlepší je však dát výrazu smysluplné jméno zástupného znaku sám,a to můžete udělat pomocí syntaxe ${expression}:placeholder:.

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

Tento zástupný název pak můžete použít kdekoli v překladech:

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

Vlastní ID

Všimněte si, že pokud máte překlady s vlastními ID,použijí se pomocí $localize (jako tomu bylo dříve):

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

Pak váš překlad vypadá takto:

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

což je samozřejmě příjemnější k použití.

A co pro překlady v kódu?“$localize rozumí také syntaxi umožňující zadat ID:

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

Syntaxe pro vlastní ID je stejná jako v šablonách,a ID je obklopeno dvojtečkami, aby bylo odděleno od obsahu překladu.

Stejně jako u syntaxe šablony můžete také zadat popis a význam,abyste překladateli pomohli s trochou kontextu: :meaning|description@@id:message.

Například:

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

Mějte na paměti, že se jedná o nízkoúrovňové, nedokumentované API. tým nebo komunita Angularu pravděpodobně nabídne funkce vyšší úrovněs lepšími zkušenostmi pro vývojáře (no, doufám v to!). locl od Oliviera Combeho, autora ngx-translate, asi stojí za to sledovat 🧐.

Překlady za běhu

Jak už jsem se zmínil,pokud používáte výše uvedené příkazy CLI (ng serve --configuration=fr nebo ng build --localize), pak je aplikace zkompilována a poté přeloženapředtím, než se dostane do prohlížeče, takže za běhu nedochází k žádnému volání $localize.

Ale $localize byl navržen tak, aby nabízel další možnost:překlady za běhu. co to znamená? No, mohli bychom dodat pouze jednu aplikaci, která by obsahovala volání $localize, a před spuštěním aplikace bychom mohli načíst požadované překlady. už žádné N sestavení a N balíčků pro N lokalit \o/

Aniž bychom zabíhali do detailů, je to možné už ve v9, a to pomocí funkce loadTranslations, kterou nabízí @angular/localize.To je však třeba provést před spuštěním aplikace.

Překlady můžete načíst v polyfills.ts pomocí:

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

Jak vidíte, není třeba brát ohled na locale:prostě načtete překlad jako objekt,jehož klíče jsou řetězce k překladu a hodnoty jejich překlady.

Nyní, když spustíte jednoduchý ng serve,nadpis se zobrazí ve francouzštině! a už není potřeba ng xi18n, ani messages.fr.xlfnebo specifická konfigurace pro každé locale v angular.json. v dlouhodobém horizontu, až to bude řádně podporováno a zdokumentováno,bychom měli být schopni načítat JSON soubory za běhu,jako to dělá většina i18n knihoven.Toho by se dalo dosáhnout i ve v9, jen je to trochu ruční práce,ale je to proveditelné.

A co potom změna locale za běhu?Můžeme načíst další sadu překladů při spuštění aplikace?No, ne. Současný způsob generování volání $localize znemožňuje jejich následnou změnu: musíte aplikaci restartovat. ale pokud vám nevadí obnovení prohlížeče, je to možné. vyzkoušel jsem jednoduchou strategii, která funguje:

  • uživatel vybere nový jazyk (například španělštinu).
  • jazyk uložíme do prohlížeče (například do localStorage)
  • znovu načteme stránku, čímž se aplikace restartuje
  • v polyfills.tszačneme načítáním uloženého jazyka
  • načteme správnou sadu překladů pro španělštinu pomocí loadTranslations.

To bude samozřejmě v budoucnu plynulejší, buď v některé z příštích verzí Angularu,nebo prostřednictvím knihovny z ekosystému. každopádně se blížíme k tomu, abychom dodávali jen jednu verzi naší aplikace a překlady načítali jen za běhu \o/

Všechny naše materiály (ebook, online školení a trénink) jsou s těmito změnami aktuální, pokud se chcete dozvědět víc!

Leave a Reply