Internasjonalisering av appen din kan gjøre programvareutvikling til en smertefull opplevelse, spesielt hvis du ikke begynner å gjøre det helt fra begynnelsen, eller hvis du tar en villig tilnærming til det.
Moderne apper, der front-end og back-end er tydelig atskilt fra hverandre, kan være enda vanskeligere å takle når det gjelder internasjonalisering. Plutselig har du ikke lenger tilgang til mengden tidstestede verktøy som en gang hjalp til med å internasjonalisere de tradisjonelle websidegenererte webappene dine.
Følgelig krever en AngularJS-app levering av internasjonalisering (i18n) og lokalisering (l10n) på forespørsel for å bli levert til klienten for å gjengi seg selv på riktig sted. I motsetning til tradisjonelle gjengitte apper på serversiden, kan du ikke lenger stole på at serveren leverer sider som allerede er lokalisert. Du kan lære om å bygge et flerspråklig PHP-program her
I denne artikkelen vil du lære hvordan du kan internasjonalisere din AngularJS app, og vil lære om verktøy du kan bruke for å lette prosessen. Å gjøre AngularJS-appen din flerspråklig kan utgjøre noen interessante utfordringer, men visse tilnærminger kan gjøre det lettere å omgå de fleste av disse utfordringene.
For å tillate klienten å endre språket og lokaliteten på grunnlag av brukerpreferanser, må du ta en rekke viktige designbeslutninger:
Å svare på disse spørsmålene så tidlig som mulig kan bidra til å unngå hindringer i utviklingsprosessen langs linjen. Hver av disse utfordringene vil bli tatt opp i denne artikkelen; noen gjennom robuste AngularJS-biblioteker, andre gjennom visse strategier og tilnærminger.
Det finnes en rekke JavaScript-biblioteker som er bygget spesielt for internasjonalisering av AngularJS-apper.
angular-translate
er en AngularJS-modul som gir filtre og direktiver, sammen med muligheten til å laste i18n-data asynkront. Den støtter pluralisering gjennom MessageFormat
, og er designet for å være svært utvidbar og konfigurerbar.
Hvis du bruker angular-translate
i prosjektet ditt, kan du finne noen av følgende pakker som veldig nyttige:
angular-sanitize
: kan brukes til å beskytte mot XSS angrep i oversettelser.angular-translate-interpolation-messageformat
: pluralisering med støtte for kjønnssensitiv tekstformatering.angular-translate-loader-partial
: brukes til å levere oversatte strenger til kunder.For en virkelig dynamisk opplevelse kan du legge til angular-dynamic-locale
til gjengen. Dette biblioteket lar deg endre lokaliteten dynamisk — og det inkluderer måten datoer, tall, valutaer osv. Er formatert på.
Forutsatt at du allerede har AngularJS-kjeleplaten klar, kan du bruke NPM til å installere internasjonaliseringspakker:
npm i -S angular-translate angular-translate-interpolation-messageformat angular-translate-loader-partial angular-sanitize messageformat
Når pakkene er installert, ikke glem å legge til modulene som appens avhengighet:
// /src/app/core/core.module.js app.module('app.core', ['pascalprecht.translate', ...]);
Merk at navnet på modulen er forskjellig fra navnet på pakken.
Anta at appen din har en verktøylinje med litt tekst og et felt med litt plassholdertekst:
Hello ...
Ovenstående visning har to biter av tekst som du kan internasjonalisere: “Hei” og “Søk”. Når det gjelder HTML, vises den ene som innteksten til et ankermerke, mens den andre vises som en verdi av et attributt.
For å internasjonalisere dem, må du erstatte begge strenglitteraturene med tokens som AngularJS deretter kan erstatte med de faktiske oversatte strengene, basert på brukerens preferanse, mens du gjengir siden.
AngularJS kan gjøre dette ved å bruke tokens til å slå opp i oversettelsestabellene du oppgir. Modulen angular-translate
forventer at disse oversettelsestabellene leveres som vanlige JavaScript-objekter eller som JSON-objekter (hvis de lastes eksternt).
Her er et eksempel på hvordan disse oversettelsestabellene generelt vil se ut:
// /src/app/toolbar/i18n/en.json { 'TOOLBAR': { 'HELLO': 'Hello', 'SEARCH': 'Search' } } // /src/app/toolbar/i18n/tr.json { 'TOOLBAR': { 'HELLO': 'Merhaba', 'SEARCH': 'Ara' } }
For å internasjonalisere verktøylinjevisningen ovenfra, må du erstatte strengbokstavene med tokens som AngularJS kan bruke til å slå opp i oversettelsestabellen:
{ translate}
Legg merke til hvordan, for indre tekst, kan du enten bruke translate
direktivet eller translate
filter. (Du kan lære mer om translate
direktivet her og om translate
filtre her .)
Med disse endringene, når visningen er gjengitt, angular-translate
vil automatisk sette inn riktig oversettelse tilsvarende TOOLBAR.HELLO
inn i DOM basert på gjeldende språk.
For å tokenisere strenglitteraturer som vises som attributtverdier, kan du bruke følgende tilnærming:
// /src/app/toolbar/i18n/en.json { 'TOOLBAR': { 'HELLO': 'Hello, {{name}}.' } }
Nå, hva om tokeniserte strengene dine inneholdt variabler?
For å håndtere saker som 'Hei, {{name}}.', Kan du utføre variabel erstatning ved hjelp av samme interpolatorsyntaks som AngularJS allerede støtter:
Oversettelsestabell:
{{'TOOLBAR.HELLO | translate:'{ name: vm.user.name }'}}
Deretter kan du definere variabelen på flere måter. Her er noen få:
He saw 1 person(s) on floor 1. She saw 1 person(s) on floor 3. Number of people seen on floor 2: 2.
Pluralisering er et ganske vanskelig tema når det gjelder i18n og l10n. Ulike språk og kulturer har forskjellige regler for hvordan et språk håndterer pluralisering i forskjellige situasjoner.
På grunn av disse utfordringene vil programvareutviklere noen ganger rett og slett ikke løse problemet (eller i det minste ikke løse det tilstrekkelig), noe som resulterer i programvare som produserer dumme setninger som disse:
He saw 1 person on the 2nd floor. She saw 1 person on the 3rd floor. They saw 2 people on the 5th floor.
Heldigvis er det en standard for hvordan du skal håndtere dette, og en JavaScript-implementering av standarden er tilgjengelig som MessageFormat .
Med MessageFormat kan du erstatte de ovennevnte dårlig strukturerte setningene med følgende:
MessageFormat
var message = [ '{GENDER, select, male{He} female{She} other{They}}', 'saw', '{COUNT, plural, =0{no one} one{1 person} other{# people}}', 'on the', '{FLOOR, selectordinal, one{#st} two{#nd} few{#rd} other{#th}}', 'floor.' ].join(' ');
godtar uttrykk som følgende:
var messageFormatter = new MessageFormat('en').compile(message); messageFormatter({ GENDER: 'male', COUNT: 1, FLOOR: 2 }) // 'He saw 1 person on the 2nd floor.' messageFormatter({ GENDER: 'female', COUNT: 1, FLOOR: 3 }) // 'She saw 1 person on the 3rd floor.' messageFormatter({ COUNT: 2, FLOOR: 5 }) // 'They saw 2 people on the 5th floor.'
Du kan lage en formatering med matrisen ovenfor, og bruke den til å generere strenger:
MessageFormat
Hvordan kan du bruke angular-translate
med angular-translate
å dra nytte av den fulle funksjonaliteten i appene dine?
I appkonfigurasjonen forteller du ganske enkelt /src/app/core/core.config.js app.config(function ($translateProvider) { $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); });
at meldingsformatinterpolering er tilgjengelig som følger:
// /src/app/main/social/i18n/en.json { 'SHARED': '{GENDER, select, male{He} female{She} other{They}} shared this.' }
Slik kan en oppføring i oversettelsestabellen se ut:
{{ 'SHARED' | translate:'{ GENDER: 'male' }':'messageformat' }}
Og i utsikten:
$translateProvider
Her må du eksplisitt angi at meldingsformatinterpolatoren skal brukes i stedet for standardinterpolatoren i AngularJS. Dette er fordi de to interpolatorene skiller seg litt ut i syntaksen. Du kan lese mer om dette her .
Nå som du vet hvordan AngularJS kan slå oversettelser for tokens fra oversettelsestabeller, hvordan vet appen din om oversettingstabellene i utgangspunktet? Hvordan forteller du appen hvilken språk / språk som skal brukes?
Det er her du lærer om core.config.js
.
Du kan gi oversettelsestabellene for hvert sted du vil støtte direkte i appens // /src/app/core/core.config.js app.config(function ($translateProvider) { $translateProvider.addInterpolation('$translateMessageFormatInterpolation'); $translateProvider.translations('en', { TOOLBAR: { HELLO: 'Hello, {{name}}.' } }); $translateProvider.translations('tr', { TOOLBAR: { HELLO: 'Merhaba, {{name}}.' } }); $translateProvider.preferredLanguage('en'); });
filen som følger:
// /src/app/toolbar/toolbar.controller.js app.controller('ToolbarCtrl', function ($scope, $translate) { $scope.changeLanguage = function (languageKey) { $translate.use(languageKey); // Persist selection in cookie/local-storage/database/etc... }; });
Her gir du oversettelsestabeller som JavaScript-objekter for engelsk (en) og tyrkisk (tr), mens du erklærer at det nåværende språket er engelsk (en). Hvis brukeren ønsker å endre språk, kan du gjøre det med $ oversett tjeneste :
// /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.determinePreferredLanguage(); });
Det er fortsatt spørsmålet om hvilket språk som skal brukes som standard. Hardkoding av startspråket i appen vår er kanskje ikke alltid akseptabelt. I slike tilfeller er et alternativ å prøve å bestemme språket automatisk ved hjelp av $ translateProvider:
determinePreferredLanguage
window.navigator
søker etter verdier i partialLoader
og velger en intelligent standard inntil et klart signal blir gitt av brukeren.
Den forrige delen viste hvordan du kan gi oversettelsestabeller direkte i kildekoden som JavaScript-objekter. Dette kan være akseptabelt for små applikasjoner, men tilnærmingen er ikke skalerbar, og det er grunnen til at oversettelsestabeller ofte lastes ned som JSON-filer fra en ekstern server.
Ved å opprettholde oversettelsestabeller på denne måten reduseres den opprinnelige nyttelaststørrelsen som leveres til klienten, men introduserer ekstra kompleksitet. Nå står du overfor designutfordringen med å levere i18n-data til klienten. Hvis dette ikke håndteres nøye, kan applikasjonens ytelse lide unødvendig.
Hvorfor er det så komplisert? AngularJS-applikasjoner er organisert i moduler. I en kompleks applikasjon kan det være mange moduler, hver med sine egne i18n-data. En naiv tilnærming, som å laste inn og levere i18n-data samtidig, bør derfor unngås.
Det du trenger er en måte å organisere i18n-dataene dine etter modul. Dette gjør at du kan laste inn akkurat det du trenger når du trenger det, og cache det som er lastet inn tidligere, for å unngå å laste inn de samme dataene (i det minste til hurtigbufferen er ugyldig).
Det er her /src/app/main/i18n/en.json /src/app/main/i18n/tr.json /src/app/toolbar/i18n/en.json /src/app/toolbar/i18n/tr.json
kommer inn i bildet.
La oss si at applikasjonens oversettelsestabeller er strukturert slik:
$translateProvider
Du kan konfigurere partialLoader
å bruke // /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.useLoader('$translatePartialLoader', { urlTemplate: '/src/app/{part}/i18n/{lang}.json' }); });
med et URL-mønster som samsvarer med denne strukturen:
$translatePartialLoader
Som man kunne forvente, erstattes “lang” med språkkoden ved kjøretid (f.eks. “En” eller “tr”). Hva med 'del'? Hvordan vet $ translateProvider hvilken 'del' som skal lastes inn?
Du kan gi denne informasjonen i kontrollere med // /src/app/main/main.controller.js app.controller('MainCtrl', function ($translatePartialLoader) { $translatePartialLoader.addPart('main'); }); // /src/app/toolbar/toolbar.config.js app.controller('ToolbarCtrl', function ($translatePartialLoader) { $translatePartialLoader.addPart('toolbar'); });
:
$translateProvider
Mønsteret er nå fullført, og i18n-dataene for en gitt visning lastes når kontrolleren først utføres, noe som er akkurat det du vil ha.
Hva med caching?
Du kan aktivere standardbufferen i appkonfigurasjonen med // /src/app/core/core.config.js app.config(function ($translateProvider) { ... $translateProvider.useLoaderCache(true); // default is false });
:
$translate
Hvis du trenger å bytte hurtigbufferen for et gitt språk, kan du bruke $translate.refresh(languageKey); // omit languageKey to refresh all
:
angular-dynamic-locale
Med disse delene på plass er applikasjonen din fullstendig internasjonalisert og støtter flere språk.
I denne delen vil du lære hvordan du kan bruke npm i -S angular-dynamic-locale angular-i18n
for å støtte formatering av brukergrensesnittelementer som tall, valutaer, datoer og lignende, i et AngularJS-program.
Du må installere to pakker til for dette:
// /src/app/core/core.module.js app.module('app.core', ['tmh.dynamicLocale', ...]);
Når pakkene er installert, kan du legge til modulen i appens avhengighet:
angular-locale_en-us.js
Lokale regler er enkle JavaScript-filer som gir spesifikasjoner for hvordan datoer, tall, valutaer og lignende skal formateres av komponenter som avhenger av $ lokal service.
Listen over for øyeblikket støttede lokaliteter er tilgjengelig her .
Her er et utdrag fra ... 'MONTH': [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], 'SHORTDAY': [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ], ...
illustrerer formatering av måned og dato:
angular-dynamic-locale
I motsetning til i18n-data, er lokale regler globale for applikasjonen, og krever at reglene for en gitt lokalitet lastes inn samtidig.
Som standard er angular/i18n/angular-locale_{{locale}}.js
forventer at filene for lokale regler skal være plassert i tmhDynamicLocaleProvider
. Hvis de er lokalisert andre steder, // /src/app/core/core.config.js app.config(function (tmhDynamicLocaleProvider) { tmhDynamicLocaleProvider.localeLocationPattern( '/node_modules/angular-i18n/angular-locale_{{locale}}.js'); });
må brukes til å overstyre standard:
tmhDynamicLocaleCache
Bufring håndteres automatisk av angular-dynamic-locale
service.
Det er mindre bekymringsfullt å ugyldiggjøre cachen her, siden det er mindre sannsynlig at lokale regler endres enn strengoversettelser.
For å bytte mellom lokaliteter, tmhDynamicLocale
gir // /src/app/toolbar/toolbar.controller.js app.controller('ToolbarCtrl', function ($scope, tmhDynamicLocale) { $scope.changeLocale = function (localeKey) { tmhDynamicLocale.set(localeKey); // Persist selection in cookie/local-storage/database/etc... }; });
service:
angular-i18n
Lokale regler sendes med import gulp from 'gulp'; import map from 'map-stream'; import rename from 'gulp-rename'; import traverse from 'traverse'; import transform from 'vinyl-transform'; import jsonFormat from 'gulp-json-format'; function translateTable(to) { return transform(() => { return map((data, done) => { const table = JSON.parse(data); const strings = []; traverse(table).forEach(function (value) { if (typeof value !== 'object') { strings.push(value); } }); Promise.all(strings.map((s) => getTranslation(s, to))) .then((translations) => { let index = 0; const translated = traverse(table).forEach(function (value) { if (typeof value !== 'object') { this.update(translations[index++]); } }); done(null, JSON.stringify(translated)); }) .catch(done); }); }); } function translate(to) { return gulp.src('src/app/**/i18n/en.json') .pipe(translateTable(to)) .pipe(jsonFormat(2)) .pipe(rename({ basename: to })) .pipe(gulp.dest('src/app')); } gulp.task('translate:tr', () => translate('tr')); This task assumes the following folder structure: /src/app/main/i18n/en.json /src/app/toolbar/i18n/en.json /src/app/navigation/i18n/en.json ...
så alt du trenger å gjøre er å gjøre pakkeinnholdet tilgjengelig for applikasjonen din etter behov. Men hvordan genererer du JSON-filene til oversettelsestabellene dine? Det er ikke akkurat en pakke du kan laste ned og plugge inn i applikasjonen vår.
Et alternativ er å bruke programmerings-API-er for oversettelser, spesielt hvis strengene i applikasjonen din er enkle bokstaver uten variabler eller flertallsuttrykk.
Med Gulp og et par ekstra pakker, som ber om programmatiske oversettelser for applikasjonen din, er en lek:
/src/app/main/i18n/en.json /src/app/main/i18n/tr.json /src/app/toolbar/i18n/en.json /src/app/toolbar/i18n/tr.json /src/app/navigation/i18n/en.json /src/app/navigation/i18n/tr.json ...
Skriptet leser først alle engelske oversettelsestabeller, ber asynkront om oversettelser for strengressursene, og erstatter deretter de engelske strengene med de oversatte strengene for å produsere en oversettelsestabell på et nytt språk.
Til slutt er den nye oversettelsestabellen skrevet som et søsken til den engelske oversettelsestabellen, og gir:
getTranslation
Implementering av import bluebird from 'bluebird'; import MicrosoftTranslator from 'mstranslator'; bluebird.promisifyAll(MicrosoftTranslator.prototype); const Translator = new MicrosoftTranslator({ client_id: process.env.MICROSOFT_TRANSLATOR_CLIENT_ID, client_secret: process.env.MICROSOFT_TRANSLATOR_CLIENT_SECRET }, true); function getTranslation(string, to) { const text = string; const from = 'en'; return Translator.translateAsync({ text, from, to }); }
er også grei:
angular-translate
Her bruker vi Microsoft Translate , men man kan bare enkelt bruke en annen leverandør som Google Oversetter eller Yandex Oversett .
Mens programmatiske oversettelser er praktiske, er det flere ulemper, inkludert:
I disse tilfellene og andre kan det være nødvendig med oversettelser av mennesker; det er imidlertid et emne for et annet blogginnlegg.
I denne artikkelen lærte du hvordan du bruker disse pakkene til å internasjonalisere og lokalisere AngularJS applikasjoner .
angular-dynamic-locale
, gulp
, og
|_+_|er kraftige verktøy for å internasjonalisere et AngularJS-program som innkapsler smertefulle implementeringsdetaljer på lavt nivå.
For en demo-app som illustrerer ideene diskutert i dette innlegget, sjekk ut dette GitHub-depot .