[EDIT: 2020-01-31] Vejledningen her fungerer, iht min erfaring for Angular 6, 7 og 8.
Oversættelse i Angular er kompliceret ved første øjekast, men efterhånden som man dykker ned i tågen af information, viser sig en ganske fornuftig arbejdsgang der i stor udstrækning forsimpler opgaven i forhold til tidligere tiders metoder.
En af de helt store fordele er at Angular i dag kan generere sprogfiler i forskellige standardformater. Dvs. man kan sende filerne til andre, som kan åbne dem i det oversættelsesprogram de måtte bruge, oversætte og returnere dem.
Læse HELE denne vejledning inden du går igang.
Indholdsfortegnelse
Installation og brug af @ngx-translate
Start med at installere The internationalization (i18n) library for Angular på følgende vis:
1 |
npm install @ngx-translate/core --save |
Nu skal du så tilføje 2 linier i src/app/app.module.ts
:
1 2 3 4 5 6 7 8 9 10 11 12 |
import {BrowserModule} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; import {TranslateModule} from '@ngx-translate/core'; // <- Tilføj @NgModule({ imports: [ BrowserModule, TranslateModule.forRoot() // <- Tilføj ], bootstrap: [AppComponent] }) export class AppModule { } |
Når du så har en tekst der skal oversættes i en template, tilføjer du ganske enkelt attributten i18n
:
1 2 |
<h1 i18n>Denne er en overskrift</h1> <p i18n>Og dette er et afsnit</p> |
Generering og oversættelse af sprogfiler
Opret en basis sprogfil
Når du er færdig med at redigere din app og er klar til at bygge den, skal der generes en basisfil til oversættelse:
1 |
ng xi18n --output-path i18n --i18n-locale da |
–output-path: relativ sti under src/
til den mappe hvor sprogfilen skal placeres
–i18n-locale: det sprog du bruger i dine templates (standard/default sprog)… i dette tilfælde dansk (da)
Ovenstående kommando opretter/overskriver messages.xlf
under src/i18n
.
Vi kan dog simplificere kommandoen og gøre den nemmere at huske ved at føje den til package.json:
1 2 3 4 5 6 7 8 |
{ [...] "scripts": { [...] "extract-i18n": "ng xi18n --output-path i18n --i18n-locale da" } [...] } |
Nu kan du nøjes med:
1 |
$ npm run extract-i18n |
Hvis du åbner src/i18n/messages.xlf
som er en XML fil, vil du se noget a la:
1 2 3 4 5 6 |
<trans-unit id="a8f10794864e49b16224b22faaf4a86229b6c53d" datatype="html"> <source>Dette er en overskrift</source> </trans-unit> <trans-unit id="1189e6770476cd4eef2d4462a98add152b80961e" datatype="html"> <source>Dette er et afsnit</source> </trans-unit> |
Nu er det så meningen at man skal lave kopier af filen, een for hvert sprog. F.eks. messages.en.xlf, messages.sv.xlf osv… og disse filer oversættes så til de respektive sprog.
Flette nye tekster med eksisterende
Men hvad nu når man fremover videreudvikler sin webapp. Dette vil resultere i en ny basisfil, som skal kopieres til de andre sprog…. men så overskriver man jo sine eksisterende oversættelser. Her kommer xliffmerge
ind i billedet. Det er et fantastisk redskab som ikke bare opretter alle dine sprogfiler for dig (du slipper for at kopiere din basisfil manuelt), og sørger for at eksisterende oversættelse blivet flettet med nye tekster.
Vi installerer den globalt, så vi også kan bruge den i andre projekter og derfor skal du gøre det som superbruger/root/admin (kært barn har mange navne):
1 |
$ sudo npm install -g ngx-i18nsupport |
Nu kan vi så udvide vores kommando i package.json en smule:
1 2 3 4 5 6 7 8 |
{ [...] "scripts": { [...] "extract-i18n": "ng xi18n --output-path i18n --locale da && xliffmerge --profile xliffmerge.json en sv nb da" } [...] } |
Her har vi føjet && xliffmerge ...
til den eksisterende kommando. Det fortæller blot hvad din xliffmerge konfig hedder og hvilke sprog du ønsker at oversætte til, i dette tilfælde engelsk, svensk og norsk. Vi føjer dog også da
til. Godt nok er dansk sproget i vores basisfil, men derfor skal vi stadig bruge en sprogfil, den skal bare ikke oversættes.
Opret xliffmerge.json i roden af dit projekt (samme sted som package.json) med følgende indhold:
1 2 3 4 5 6 7 8 |
{ "xliffmergeOptions": { "srcDir": "src/i18n", "genDir": "src/i18n", "defaultLanguage": "da", "allowIdChange": true } } |
Bemærk at stien til ng xi18n
‘s output-path er relativ i forhold til src/
men at stien til xliffmerge
er relativ i forhold til projektets rodmappe og her skriver vi derfor src/i18n
i srcDir og genDir. Dette kan godt give anledning til forvirring første gang.
Se alle konfigurationsmulighederne til xliffmerge
her: https://github.com/martinroob/ngx-i18nsupport
Så kører vi igen…
1 |
$ npm run extract-i18n |
Du får nogle advarsler i output, men det er blot information…
1 2 3 4 |
WARNING: please translate file "src/i18n/messages.en.xlf" to target-language="en" WARNING: please translate file "src/i18n/messages.sv.xlf" to target-language="sv" WARNING: please translate file "src/i18n/messages.nb.xlf" to target-language="nb" WARNING: please translate file "src/i18n/messages.da.xlf" to target-language="da" |
Nu har du 5 filer i src/i18n:
messages.xlf
basisfilen med alle tekster fundet i din app.messages.en.xlf
den engelske version.messages.nb.xlf
den norske version.messages.sv.xlf
den svenske version.messages.da.xlf
den danske version.
Det smarte er, at alle nye eller ændrede tekster nu er markeret og dermed nemme at finde:
1 |
<target state="new">Dette er en ny eller ændret tekst, der skal oversættes</target> |
Bemærk attributten state
. Den er enten new, translated
eller final
.
xliffmerge
sætter state til new
, og du skal så selv sørge for at ændre værdien til translated
når en tekst er oversat. Næste gang du laver sprogfiler, vil du dermed kunne skelne nye og allerede oversatte tekster fra hinanden.
Se også: Tutorial for using xliffmerge with angular cli
Tjek et sprog i browseren
Hvis du nu gerne vil se din norske oversættelse, kan det sagtens lade sig gøre med ng serve
. Men først laver vi lige lidt konfiguration, så det bliver nemt:
Åben ~/mitProjekt/angular.json
. Du skal føje nogle nye sektioner til under:
projects -> architect -> build -> configurations
… og …
projects -> architect -> serve -> configurations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "mitProjekt": { ... }, "architect": { "build" : { "builder": { ... }, "options": { ... }, "configurations": { "da": { //<- *** Herfra *** "aot":true, "outputPath":"dist/mitProjekt-da", "baseHref": "/da/", "i18nFile": "src/i18n/messages.da.xlf", "i18nLocale": "da", "i18nFormat": "xlf", "i18nMissingTranslation": "[Fejl]" }, "nb": { "aot":true, "outputPath":"dist/mitProjekt-nb", "baseHref": "/nb/", "i18nFile": "src/i18n/messages.nb.xlf", "i18nLocale": "nb", "i18nFormat": "xlf", "i18nMissingTranslation": "[Feil]" }, [... osv] //<- *** Hertil *** } }, "serve": { "builder": "...", "options": { ... }, "configurations": { "production": { ... }, "da": { //<- *** Herfra *** "browserTarget": "mitProjekt:build:da" }, "nb": { "browserTarget": "mitProjekt:build:nb" }, [... osv] //<- *** Hertil *** } } } } } |
… og nu kan vi føje vores serve
kommandoer til package.json
:
1 2 3 4 5 6 7 8 9 10 11 12 |
{ [...] "scripts": { [...] "extract-i18n": "ng xi18n --output-path i18n --i18n-locale da && xliffmerge --profile xliffmerge.json en sv nb", "serve-nb": "ng serve -o --configuration=nb", "serve-sv": "ng serve -o --configuration=sv", "serve-da": "ng serve -o --configuration=da", "serve-en": "ng serve -o --configuration=en", } [...] } |
Dvs. for at se din app’en engelsk i dit udviklingsmiljø, skal du blot:
1 |
$ npm run serve-en |
Bygge en internationaliseret app
Normalt når man bygger en app bliver det færdige projekt placeret direkte i ~/mitProjekt/dist
. Men når du har en app med flere sprog, bygges een app hvor hver sprog og derfor konfigurerede vi bygge-processen til at placere hvert sprog i sin egen mappe: dist/[sprog]
f.eks. dist/da/
, dist/en/
osv… (se angular.json).
Brugere af den danske version benytter så https://mitdomæne.dk/da/ og bruger af den engelske på https://mitdomæne.dk/en/ osv…
Du kan også bygge app’en så alle sprog er inkluderet, men hvis dine sprogfiler er omfattende betyder det at app’en fylder en del mere og det kan også have indflydelse på app’ens ydeevne. Dette er derfor ikke omfattet af denne vejledning.
Vi mangler lige at føje bygge-kommandoerne til package.json. Det er de linier herunder der starter med ng build
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ [...] "scripts": { [...] "extract-i18n": "ng xi18n --output-path i18n --i18n-locale da && xliffmerge --profile xliffmerge.json en sv nb", "serve-nb": "ng serve -o --configuration=nb", "serve-sv": "ng serve -o --configuration=sv", "serve-da": "ng serve -o --configuration=da", "serve-en": "ng serve -o --configuration=en", "build-nb": "ng build --configuration=nb", //<- *** herfra *** "build-sv": "ng build --configuration=sv", "build-da": "ng build --configuration=da", "build-en": "ng build --configuration=en" } [...] } |
Dvs. for at bygge dit dev miljø, kan du nu blot:
1 2 3 4 |
$ npm run build-nb $ npm run build-sv $ npm run build-da $ npm run build-en |
… du skal naturligvis også føje kommandoer til produktion i package.json. Disse konfigurationer skal naturligvis også føjes til angular.json filen.
1 2 3 4 |
"build-prod-nb": "ng build --prod --configuration=production-nb", "build-prod-sv": "ng build --prod --configuration=production-sv", "build-prod-da": "ng build --prod --configuration=production-da", "build-prod-en": "ng build --prod --configuration=production-en" |
… som så kan afvikles med:
1 |
$ npm run build-prod-nb |
En typisk “production” konfiguration kunne se således ud:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
"production-da": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "outputPath":"dist-prod/da", "baseHref": "/da/", "i18nFile": "src/i18n/messages.da.xlf", "i18nLocale": "da", "i18nFormat": "xlf", "i18nMissingTranslation": "[Fejl]", "optimization": true, "outputHashing": "all", "sourceMap": false, "extractCss": true, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true }, |
Bemærk at vi vha. “fileReplacements” skifter environment fil. Jeg har også valgt at oprette en særlig dist-prod mappe (se outputPath).
Vi kan også slippe for at skrive npm run build-[sprog]
4 gange:
1 2 3 |
$ for lang in nb sv da en; do \ npm run build-$lang; \ done |
… eller gøre det endnu kortere ved at smide ovenstående i package.json
og lave en build-all
kommando:
1 |
"build-all": "for lang in nb sv da en; do npm run build-$lang; done" |
Dvs. du nu kan bygge alle 4 apps ved blot at skrive:
1 |
$ npm run build-all |
Nice! 🙂
Automatisk valg af sprog for brugeren
Hvis brugeren lander på https://mitdomæne.dk/ kan Apache viderestille til det sprog som er angivet i brugerens browser. Det er naturligvis ikke sikkert det er det sprog brugeren ønsker, så det skal kombineres med en mulighed for at vælge sprog, her et eksempel på en sådan konfiguration.
Hvis brugerens browser accepterer fransk og spansk (fr og es) sendes den videre til henholdsvis myapp.com/fr/
eller myapp.com/es/
. I alle andre tilfælde sendes brugeren til myapp.com/en/
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<VirtualHost *:80> ServerName www.myapp.com DocumentRoot /var/www <Directory "/var/www"> RewriteEngine on RewriteBase / RewriteRule ^../index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (..) $1/index.html [L] RewriteCond %{HTTP:Accept-Language} ^fr [NC] RewriteRule ^$ /fr/ [R] RewriteCond %{HTTP:Accept-Language} ^es [NC] RewriteRule ^$ /es/ [R] RewriteCond %{HTTP:Accept-Language} !^es [NC] RewriteCond %{HTTP:Accept-Language} !^fr [NC] RewriteRule ^$ /en/ [R] </Directory> </VirtualHost> |
Kilde: Deploying an i18n Angular app with angular-cli
Sammenfatning af arbejdsgangen
Hvis du har fulgt denne vejledning, vil din arbejdsgang fremover være som følger:
- Ændringer foretages
npm run extract-i18n
- Oversæt filer og ændre
state
attributten franew
tiltranslated
- Brug
npm run serve-[sprog]
i dit udviklingsmiljø for at se app’en i et bestemt sprog - Brug
npm run build-[sprog]
ellernpm run build-prod-[sprog]
for at bygge app’en - … forfra 🙂