Daniel Crisp

Pro­vid­ing more than sin­gle lan­guage sup­port en­sures your con­tent can be read by more of the world’s on­line pop­u­la­tion

Web Designer - - Contributors -

Daniel is a se­nior fron­tend de­vel­oper at a startup in Lon­don’s Ca­nary Wharf. In this is­sue he takes a look at An­gu­lar’s built-in in­ter­na­tion­al­i­sa­tion tools and shows you how to use them in your pro­jects.

In this tu­to­rial we’re go­ing to take you through the process of mak­ing your app ac­ces­si­ble and user friendly for peo­ple around the world. Only about 20% of the world speaks English so pro­vid­ing other lan­guage op­tions can greatly in­crease your app’s reach. We’re go­ing to take a look at An­gu­lar’s built-in in­ter­na­tion­al­i­sa­tion tools and show you how to cor­rectly use them.

We’ve cre­ated a very sim­ple demo app to demon­strate the process. Clone it from here and then fol­low the in­stal­la­tion in­struc­tions: https://github.com/daniel­crisp/an­gu­lar-i18n-demo

Start the app to fa­mil­iarise your­self with it. It just dis­plays and up­dates ran­dom num­bers and val­ues with dif­fer­ent con­texts, eg cur­ren­cies, dates etc. We’ll cover some of the pipes and fea­tures used dur­ing the tu­to­rial.

1. An in­tro­duc­tion

There are two words that are of­ten used in­ter­change­ably when talk­ing about trans­lat­ing an app – in­ter­na­tion­al­i­sa­tion and lo­cal­i­sa­tion – how­ever, they ac­tu­ally mean slightly dif­fer­ent things. In­ter­na­tion­al­i­sa­tion refers to the process of pre­par­ing your app for sup­port­ing dif­fer­ent lan­guages. In con­trast, lo­cal­i­sa­tion refers to the process of ac­tu­ally trans­lat­ing your app into your re­quired lan­guages. Es­sen­tially in­ter­na­tion­al­i­sa­tion is some­thing you do once per app, and lo­cal­i­sa­tion hap­pens once per lo­cale – at least that’s the plan.

Th­ese terms might also be fa­mil­iar in their short­ened ver­sions: i18n (where 18 is the num­ber of let­ters be­tween the first ‘i’ and the last ‘n’ of in­ter­na­tion­al­i­sa­tion) and l10n (where 10 is the num­ber of let­ters be­tween the ‘i’ and the ‘n’ of lo­cal­i­sa­tion).

2. What’s lo­cal­i­sa­tion?

There are over 6,000 lan­guages used around the world to­day, most of which are only used by very small groups of peo­ple. Yet even if we only fo­cus on the top three lan­guages – Man­darin, Span­ish and English – there will be sig­nif­i­cant dif­fer­ences in date for­mat­ting, gram­mat­i­cal struc­ture, plu­ral­i­sa­tion and num­ber for­mat­ting.

If we in­clude the fifth most widely used lan­guage – Ara­bic – we en­counter an­other dif­fer­ence; Ara­bic is a right-to-left (RTL) script which means the UI will also have to be mir­rored.

So dur­ing lo­cal­iza­tion we have to con­sider gram­mar, lay­out and for­mat­ting dif­fer­ences, and of course, we also have to change the text it­self. An­gu­lar can help with much of this but you’ll still need to man­u­ally trans­late the text.

3. Lo­cales

We will need to lo­calise for each lo­cale we need to sup­port. A lo­cale refers to the gen­eral set of pref­er­ences for the con­sid­er­a­tions men­tioned above that tend to be shared within a re­gion of the world, typ­i­cally a coun­try. Each lo­cale is rep­re­sented by a Uni­code lo­cale iden­ti­fier, which spec­i­fies the lan­guage code and the lo­cale ex­ten­sion.

An­gu­lar’s de­fault lo­cale is ‘en-us’, which is the lan­guage code ‘en’ (English) as spo­ken in the re­gion ‘US’ (United States of Amer­ica). An app lo­calised for ‘en-us’ will be subtly dif­fer­ent from an app lo­calised for ‘en-gb’ which is English as spo­ken in Great Bri­tain. For ex­am­ple, in the US dates are (baf­flingly) for­mat­ted mm/dd/yyyy, whereas here in the UK we use the more sen­si­ble dd/mm/yyyy ap­proach. This mi­nor dif­fer­ence can re­sult in a ma­jor er­ror in com­pre­hen­sion.

To make things in­ter­est­ing let’s lo­calise our demo app for Ara­bic as spo­ken in Iraq, aka ‘ar-iq’ and English as spo­ken in the UK, aka ‘en-gb’. We’ll use English as the de­fault this time.

4. Build con­fig­u­ra­tion

Our demo project was cre­ated us­ing An­gu­lar CLI, which in­cludes some use­ful tool­ing. We’re go­ing to use the Ahead-of-time (AOT) com­piler for this project so we need to make some changes to the CLI’S con­fig­u­ra­tion file: ‘an­gu­lar.json’. If you want to use Just-in-time (JIT) you need to con­fig­ure things slightly dif­fer­ently.

With an AOT build you get a small, faster ren­der­ing ready-to-go ap­pli­ca­tion which loads with­out the need for asyn­chro­nous re­quests to fetch things like tem­plates and stylesheets. As a re­sult you must cre­ate a build for each lo­cale and serve the ap­pro­pri­ate build us­ing the URL or some kind of server-side lan­guage de­tec­tion logic. The simplest ap­proach is to cre­ate a direc­tory for each lo­cale, eg www.ex­am­ple.com/en-gb and www.ex­am­ple.com/ ar-iq. The trade off is that you can’t switch lan­guage on-the-fly, but in re­al­ity that is un­likely to be some­thing re­quired by real users.

First of all we need to add a build con­fig­u­ra­tion for our Ara­bic lo­cale. In the JSON file look for the ‘ar­chi­tect.build. con­fig­u­ra­tions’ ob­ject. Add the fol­low­ing block to de­fine a con­fig­u­ra­tion for the lo­cale:

"ar-iq": {

"basehref": "/ar-iq/",

"de­ployurl": "/ar-iq/",

"out­put­path": "dist/an­gu­lar-i18n-demo/ ar-iq",

"i18n­file": "src/lo­cale/mes­sages.ar-iq. xlf",

"i18n­for­mat": "xlf",

"i18n­lo­cale": "ar-iq"

This con­fig­u­ra­tion tells An­gu­lar where to out­put the com­piled build and which trans­la­tions file and for­mat to use. It also sets the lo­cale and tells An­gu­lar which direc­tory the app will be de­ployed to.

We also need to mod­ify the de­fault op­tions in ‘ar­chi­tect.build.op­tions’ to use the ‘en-gb’ lo­cale. Set the fol­low­ing prop­er­ties as shown. Note we’re en­abling AOT here across the board so it will be used for pro­duc­tion and de­vel­op­ment builds:

"out­put­path": "dist/an­gu­lar-i18n-demo/ en-gb",

"i18n­lo­cale": "en-gb",

"de­ployurl": "/en-gb/",

"basehref": "/en-gb/",

"aot": true

An­gu­lar sup­ports a num­ber of lo­cales. Make sure you use the cor­rect value for the ‘i18n­lo­cale’ prop­erty. You can see the com­plete list here: https://github.com/an­gu­lar/ an­gu­lar/tree/master/pack­ages/com­mon/lo­cales

Be­hind the scenes the above con­fig­u­ra­tions sim­ply load and read from one of th­ese lo­cale pref­er­ence files.

5. Serve con­fig­u­ra­tion

In ad­di­tion to con­fig­ur­ing the build out­put we also need to set up the con­fig­u­ra­tion for the ‘ng serve’ com­mand for de­vel­op­ment. This is more straight­for­ward as we can sim­ply ref­er­ence the build con­fig­u­ra­tion we just added. In ‘an­gu­lar.json’ add the fol­low­ing block to ‘ar­chi­tect.serve. con­fig­u­ra­tions’:

“ar-iq”: {

“browser­tar­get”: “an­gu­lar-i18n­demo:build:ar-iq”,

“servepath”: “/ar-iq/”

}

Here we are re­fer­ring the build con­fig­u­ra­tion op­tions us­ing the ‘browser­tar­get’ prop­erty, and we’re also set­ting

the ‘servepath’. Be­fore we can ei­ther serve or build the Ara­bic app we need to cre­ate the trans­la­tions file ref­er­enced in the ‘i18n­file’ prop­erty above. An­gu­lar CLI in­cludes a tool for ex­tract­ing flagged text into an in­dus­try-stan­dard trans­la­tion source file.

We’ll cover th­ese files in more de­tail later on in the tu­to­rial but for now we just need to ex­port the ba­sic, empty file to al­low us to com­pile.

We’ll use the ‘ng xi18n’ com­mand with the fol­low­ing op­tions. This is the only time we’ll in­clude the lo­cale ID in the ‘--out-file’ file­name:

$ ng xi18n --out­put-path lo­cale --out-file mes­sages.ar-iq.xlf --i18n-lo­cale ar-iq

This should cre­ate a file in a src/lo­cale direc­tory. From now on we’ll al­ways out­put the file named ‘mes­sages.xlf’ and man­u­ally copy it over the ver­sion with the lo­cale ID in the name. The rea­son for this is to pre­vent the ex­trac­tion tool from over­writ­ing any ex­ist­ing trans­la­tions we’ve added to the file.

6. Switch­ing con­fig­u­ra­tion

At this point we can now com­pile the project and see what hap­pens, but we need to tell the ‘ng serve’ com­mand which con­fig­u­ra­tion to use. First let’s take a look at the English ver­sion. No changes here be­cause English is the de­fault:

$ ng serve

As you can see it looks much like the orig­i­nal ver­sion, which uses An­gu­lar’s de­fault lo­cale of ‘en-us’. The no­table dif­fer­ence is the cur­rency now spec­i­fies US$ in­stead of just $. Okay, now let’s try the Ara­bic ver­sion. Stop the English ver­sion and run:

$ ng serve --con­fig­u­ra­tion=ar-iq

As you’d ex­pect there are more ob­vi­ous dif­fer­ences in this ver­sion, in par­tic­u­lar the date is now writ­ten in Ara­bic. An­gu­lar can do this be­cause the names of some things, such as months and days, are from a set list and ul­ti­mately they re­late to a known num­ber. Ev­ery­thing else, how­ever, is still in English.

7. Lo­cale-aware pipes

Take a closer look at the source code of ‘app.com­po­nent. html’ and you’ll see that we use a num­ber of dif­fer­ent pipes. The fol­low­ing An­gu­lar pipes are lo­cale-aware, mean­ing that they adapt their out­put based on the cur­rent lo­cale: ‘Datepipe’, ‘Cur­ren­cyp­ipe’, ‘Dec­i­malpipe’ and ‘Per­cent­pipe’.

If you use th­ese pipes care­fully An­gu­lar will han­dle a lot of the lo­cal­i­sa­tion leg­work for you. By care­fully we mean use the avail­able pre­de­fined op­tions wher­ever you can. A good ex­am­ple is the US vs UK date for­mat­ting we men­tioned ear­lier. If you’re in the UK and you want to dis­play a date us­ing the (sen­si­ble) day-month-year for­mat, you might be frus­trated to find that the pre­de­fined ‘’short­date’’ op­tion ren­ders as m/d/yy (eg. 10/9/18) and be tempted to hard­code your de­sired for­mat like this:

{{ my­date | date:’dd/mm/y’ }}

But we now know that we get the m/d/yy for­mat be­cause An­gu­lar uses the ‘en-us’ lo­cale by de­fault. So in­stead of hard­cod­ing the for­mat we should use the ‘’short­date’’ op­tion and lo­calise our app to use ‘en-gb’.

{{ my­date | date:’short­date’ }}

It takes a tiny bit more ef­fort but then we can add lo­cales to our heart’s con­tent and al­ways have a user-friendly date for­mat.

8. over­rid­ing the pre­de­fined op­tions

Un­for­tu­nately it doesn’t seem that there is an easy, built-in way to over­ride a pre­de­fined for­mat. For ex­am­ple you can’t just de­cide that you’d pre­fer the ‘’short­date’’ for­mat to be dd/mm/yyyy in­stead of dd/mm/y as there is no way to mod­ify the for­mat at run­time. Also you can’t add your own pre­de­fined op­tions.

For th­ese edge cases you could cre­ate a cus­tom date pipe which wraps the An­gu­lar ‘Datepipe’ and han­dles any cus­tom for­mats per-lo­cale. Any­thing it doesn’t recog­nise would be passed on to the built-in ‘Datepipe’.

9. cur­ren­cyp­ipe

Off the shelf the ‘Cur­ren­cyp­ipe’ will for­mat a num­ber as US Dol­lars, trim to two dec­i­mal places and add group­ings as de­fined in the lo­cale’s pref­er­ences.

You’ll no­tice that in both our lo­cales the cur­rency is al­ways in US Dol­lars. It doesn’t mag­i­cally switch to Ster­ling (GBP) when you use the ‘en-gb’ lo­cale. The rea­son for this is that £10 is not the same as $10, so you must ex­plic­itly spec­ify the cur­rency your num­ber refers to.

Let’s up­date ‘app.com­po­nent.html’ to use GBP through­out. When spec­i­fy­ing the cur­rency code you must use the cor­rect value from the ISO 4217 stan­dard (list avail­able on­line).

Mod­ify the two cur­rency pipes by adding ‘:‘GBP’’ like so:

{{ value$ | async | cur­rency:’gbp’ }}

And you’ll start see­ing the £ sym­bol in­stead of US$.

Re­mem­ber, it doesn’t do any­thing clever like au­to­mat­i­cally con­vert USD to the equiv­a­lent value in

GBP if you change the cur­rency – it just changes the sym­bol it uses.

10. trans­la­tion work­flow

Okay, so we’ve got our two lo­cales con­fig­ured and An­gu­lar is help­fully do­ing some of the work for us out of the box, but the text is all still in English. An­gu­lar can’t trans­late this au­to­mat­i­cally sadly but it can help us with parts of the work­flow. This is what has to hap­pen:

• Flag static text in all com­po­nents for trans­la­tion • Ex­port trans­la­tion file con­tain­ing this static text • Mod­ify the trans­la­tion file and add the rel­e­vant trans­la­tions

• Merge trans­lated trans­la­tion file back into app

An­gu­lar helps us with steps 2 and 4, but as de­vel­op­ers we need do step 1 man­u­ally. Step 3 would typ­i­cally be com­pleted by a trans­la­tion pro­fes­sional or agency, us­ing spe­cial soft­ware to read and up­date the trans­la­tion file.

11. Axis de­tails

To achieve this we have to add a spe­cial at­tribute to ev­ery el­e­ment that con­tains fixed text to be trans­lated. To be clear if the con­tent ar­rives from an API then that isn’t fixed text and you’d need to lo­calise that in the API. You only

need to add the at­tribute when the text is writ­ten di­rectly in the HTML tem­plate in your source code. A key point here is that you should try to keep your Type­script files lo­cale-ag­nos­tic – in other words, avoid putting any text that needs to be trans­lated in the com­po­nent logic and keep it all in the tem­plates. Other­wise the ex­trac­tion tool won’t be able to ex­tract it. It’s good prac­tice any­way to sep­a­rate your con­cerns – in life and in code.

Let’s open up ‘app.com­po­nent.html’ and start with the ‘Cur­rent value’ ti­tle. Sim­ply add the ‘i18n’ at­tribute to the el­e­ment that di­rectly con­tains the text.

<div class=”meta__ti­tle” i18n>

Cur­rent value

</div>

It’s im­por­tant to un­der­stand that this is just a ‘dumb’ cus­tom at­tribute. It isn’t an An­gu­lar di­rec­tive that trig­gers any­thing at run­time, in fact ,the com­piler re­moves it af­ter trans­la­tion.

Any­way, let’s see what hap­pens when we run the ex­trac­tion tool again to re­gen­er­ate the trans­la­tion file. Re­mem­ber ‘--out-file’ is just ‘mes­sages.xlf’ now:

$ ng xi18n --out­put-path lo­cale --out-file mes­sages.xlf --i18n-lo­cale ar-iq

Open up the out­put XLF file and you should see a new trans­la­tion unit block that looks some­thing like this with some ad­di­tional con­text in­for­ma­tion:

<trans-unit id=”face3d45c0f0cd38b726e7798da15 3e2f8d55551” datatype=”html”>

<source>

Cur­rent value

</source>

Great, that means the tool picked up the ‘i18n’ at­tribute. That long ID is gen­er­ated by the tool and will stay the same un­less the text changes. If you have mul­ti­ple in­stances of ex­actly the same text they will all get the same ID. Don’t edit this ID!

If you pre­fer, you can spec­ify a cus­tom ID within the ‘i18n’ at­tribute. If you do this the ID will re­main the same even if the text changes, so you need to be sure you don’t have any ID col­li­sions through­out your app. Use the ‘@@’ pre­fix to set a cus­tom ID. Here the ID will be­come ‘ti­tle’:

<div class=”meta__ti­tle” i18n=”@@ti­tle”>

Cur­rent value

</div>

12. Adding some con­text

To en­sure the trans­la­tor is able to pro­vide an ac­cu­rate trans­la­tion they will of­ten need to know the con­text that the text is be­ing used in. The ‘i18n’ at­tribute al­lows us to de­fine a de­scrip­tion and a mean­ing to help the trans­la­tor. The for­mat is as fol­lows:

<div i18n=”mean­ing|de­scrip­[email protected]@ cus­to­mid”>text</div>

Let’s up­date our ti­tle with a mean­ing and de­scrip­tion:

<div class=”meta__ti­tle” i18n=”card ti­tle|value at this mo­ment in [email protected]@ti­tle”>

Cur­rent value

</div>

That should give the trans­la­tor enough con­text to pro­vide an ac­cu­rate trans­la­tion. Re­gen­er­ate the trans­la­tion file and you should see th­ese val­ues have been out­put. It’s worth not­ing that if you don’t use a cus­tom ID the gen­er­ated ID takes the mean­ing and the text into ac­count. So the same text, but with a dif­fer­ent mean­ing, will get a dif­fer­ent ID. The de­scrip­tion, how­ever, has no im­pact on the ID.

13. text with vari­ables

Let’s move on to the in­tro sec­tion. The first para­graph con­tains text and a vari­able which will be in­ter­po­lated at run­time. How do we han­dle this?

Well hap­pily it is quite straight­for­ward. Again we need to add a mean­ing­ful ‘i18n’ at­tribute to the con­tain­ing el­e­ment. Add it di­rectly to the para­graph el­e­ment:

<p i18n=”clos­ing value|value when the mar­ket closed yes­ter­[email protected]@clos­ing­value”>

Run the ex­trac­tion tool again and you’ll see this new trans­la­tion unit:

<trans-unit id=”clos­ing­value” datatype=”html”>

<source>yes­ter­day&apos;s clos­ing value was <x ID=”INTERPOLATION” equiv-text=”{{ clos­ing­value | cur­rency:&apos;gbp&apos; }}”/>.</source>

See how the vari­able interpolation has been de­tailed in the out­put. The nice thing about this is it al­lows the trans­la­tor to mod­ify the gram­mat­i­cal struc­ture of the sen­tence if nec­es­sary, with­out break­ing the bind­ing. For ex­am­ple, there may be a lan­guage where the sen­tence would be best writ­ten: X value was yes­ter­day’s clos­ing, ie with the vari­able at the start.

14. Plu­ral­i­sa­tion

Mov­ing on to the next para­graph you’ll see some in­tim­i­dat­ing syn­tax. This is called ICU Mes­sage For­mat and it al­lows you to spec­ify dif­fer­ent chunks of text based on the value of a vari­able.

You can use this to add the ‘s’ to words in English when the value is zero or not one. For ex­am­ple, if ‘sec­onds’ is a vari­able con­tain­ing the num­ber of sec­onds we can use this ICU plu­ral­i­sa­tion ex­pres­sion:

{{ sec­onds }} {sec­onds, plu­ral, one

{se­cond}, other {sec­onds}}

Which will out­put:

• 0 sec­onds • 1 se­cond

• 2 sec­onds etc

It doesn’t ap­pear to be doc­u­mented but you can also use the ‘Asyncpipe’ in­side the plu­ral­i­sa­tion syn­tax to work with Ob­serv­ables.

In that ex­am­ple ‘one’ and ‘other’ are plu­ral­i­sa­tion cat­e­gories. There are a num­ber of cat­e­gories to choose from, but be­ware! Not all lo­cales sup­port all the cat­e­gories, and An­gu­lar doesn’t tell you if you try to use a cat­e­gory that isn’t sup­ported by the cur­rent lo­cale. It is easy to end up think­ing that you’ve done some­thing wrong be­cause the ‘two’ cat­e­gory isn’t work­ing in your ‘en-gb’ lo­cale and in­stead you are see­ing the ‘other’ text. In­ex­pli­ca­bly ‘en’ (and many other com­mon lan­guages) only sup­port ‘one’ and ‘other’, even though ‘zero’ and ‘two’ are ex­plicit val­ues.

Check out this file to see what’s ac­tu­ally sup­ported: https://github.com/an­gu­lar/an­gu­lar/blob/master/ pack­ages/com­mon/src/i18n/lo­cal­iza­tion.ts

15. the mul­ti­ple ra­dial bar charts

We can work­around this lim­i­ta­tion by us­ing num­bers in­stead of cat­e­gories. Just pre­fix the value with an ‘=’:

There {watch­ers, plu­ral, =0 {is no­body} =1 {is one per­son} =2 {are two peo­ple} other {are {{ watch­ers }} peo­ple}} watch­ing right now.

This is al­ready set up in the demo app, we just need to add the ‘i18n’ at­tribute to the con­tain­ing para­graph:

<p i18n=”watch­ers|num­ber of peo­ple watch­ing the [email protected]@watch­ers”>

Run the ex­trac­tion tool again to see how this looks. You’ll see that this is out­put slightly dif­fer­ently. It will cre­ate two trans­la­tion units; one for the ICU ex­pres­sion it­self and one which in­ter­po­lates that ex­pres­sion into the orig­i­nal string.

16. Se­lect

If you want to dis­play dif­fer­ent text de­pend­ing on the value of a vari­able you can use a ‘se­lect’ ICU ex­pres­sion which is very sim­i­lar to the ‘plu­ral’ syn­tax demon­strated above. In our demo app we mon­i­tor the change ap­plied to the value and cre­ate an Ob­serv­able stream called

‘trend$’ which out­puts ‘up’, ‘down’ or ‘sta­ble’ de­pend­ing on whether the change is pos­i­tive, neg­a­tive or zero.

We then hook up our ‘se­lect’ ICU ex­pres­sion to out­put a dif­fer­ent string de­pend­ing on the stream value. Here you can see the ‘Asyncpipe’ in use:

The value {trend$ | async, se­lect, up {in­creased} down {de­creased} sta­ble

{didn’t change}}.

This is a some­what cleaner syn­tax than us­ing ‘ngif’ or ‘ngswitch’ to ma­nip­u­late the DOM, plus it also plays nicely with the ex­trac­tion tool. Add the ‘i18n’ at­tribute to the con­tain­ing el­e­ment:

<div class=”card__info” i18n=”value trend|de­scribes the value change [email protected]@trend”>

Re­gen­er­ate the trans­la­tions file and you’ll see the ap­proach is sim­i­lar to the plu­ral out­put, with two trans­la­tion units cre­ated. ICU ex­pres­sions are pretty handy once you get used to them, plus you can nest them to cre­ate more com­plex out­puts.

17. Adding trans­la­tions

One more ‘i18n’ at­tribute to add:

<div class=”card__info” i18n=”trans­ac­tions count|num­ber of trans­ac­tions to­[email protected]@ trans­ac­tions”>

Trans­ac­tions: {{ trans­ac­tions$ | async | num­ber }}

</div>

Now we’ve marked up all the text that needs trans­lat­ing we can gen­er­ate the trans­la­tion file one last time. Once it is cre­ated re­name it to ‘mes­sages.ar-iq.xlf’ and re­place the pre­vi­ous in­car­na­tion. This is the file we’d be send­ing to the trans­la­tion pro­fes­sional, but for the pur­poses of this tu­to­rial, Google Trans­late will be stand­ing in!

Open up the XLF file and du­pli­cate ev­ery ‘<source>’ el­e­ment, re­nam­ing it ‘<tar­get>’. Un­for­tu­nately it can be quite un­tidy so it might help to beau­tify the con­tents.

To check we’ve got them all, save the file and start the app with the Ara­bic lo­cale:

$ ng serve --con­fig­u­ra­tion=ar-iq

If you see any mes­sages in the ter­mi­nal like this that means you’ve missed one:

ER­ROR in xliff parse er­rors: Mes­sage *id* misses a trans­la­tion (“

Hope­fully you won’t have any er­rors and you’ll be able to see the app in the browser. We’ve not added any ac­tual Ara­bic yet so it won’t look much dif­fer­ent.

18. Google trans­late

Let’s start with some­thing easy – the ‘Cur­rent value’ ti­tle. Google Trans­late tells me it should be (Ara­bic text here) so up­date the value in the ‘<tar­get>’ el­e­ment:

<source>cur­rent value</source>

<tar­get>ara­bic text here</tar­get>

So far, so good. Now let’s do one with interpolation. Here is “Yes­ter­day’s clos­ing value was…” (hope­fully!):

<tar­get>ara­bic text here<x ID=”INTERPOLATION” equiv-text=”{{ clos­ing­value | cur­rency:&apos;gbp&apos; }}” />.</tar­get>

Use a num­ber when you trans­late so you can see where the interpolation should be. No­tice that when you see the trans­lated re­sult in Google Trans­late it will ap­pear re­versed – ie the num­ber at the start – but when you copy and paste it into the trans­la­tion file it will re­turn to the orig­i­nal order. This is hap­pen­ing be­cause Ara­bic is an RTL lan­guage so the script is (al­most) en­tirely mir­rored. Google Trans­late does this by adding a ‘dir=”rtl”’ at­tribute to the con­tain­ing el­e­ment. We’ll learn how to do this in the next step. The rest of the trans­la­tions are avail­able in the demo repo, ‘tu­to­rial’ branch.

19. Script di­rec­tion

We need to man­age the script di­rec­tion in our app be­cause An­gu­lar won’t do this au­to­mat­i­cally for us. There also doesn’t ap­pear to be any way to de­tect if the cur­rent lo­cale is an LTR or RTL lan­guage, so we’ll need to hard­code this. It’d be great if An­gu­lar of­fered a built-in di­rec­tive for this.

Open up ‘app.com­po­nent.ts’. Im­port ‘In­ject’, ‘LOCALE_ID’ and ‘Host­bind­ing’ from ‘‘@an­gu­lar/core’’. Then set up the ‘Host­bind­ing’ as fol­lows. This will add a ‘dir’ at­tribute to the Ap­p­com­po­nent and set the de­fault lan­guage di­rec­tion to ‘ltr’:

@Host­bind­ing(‘attr.dir’) dir = ‘ltr’;

Next add a con­struc­tor and in­ject the ‘LOCALE_ID’. Re­mem­ber this is set by our con­fig­u­ra­tion be­cause we’re us­ing AOT.

con­struc­tor (@IN­JECT(LOCALE_ID) pri­vate lo­cale: string) {}

And fi­nally add the fol­low­ing snip­pet to the ex­ist­ing ‘ngo­ninit’ method. Here we are check­ing if the ‘LOCALE_ID’, ie ‘ar-iq’, starts with ‘ar’ and if it does change the di­rec­tion to ‘rtl’ in­stead.

if (this.lo­cale.startswith(‘ar’)) {

this.dir = ‘rtl’;

}

If you plan to sup­port more lo­cales then you’ll prob­a­bly need to refac­tor this to make it more scal­able, how­ever, as there are only about ten RTL lan­guages in use to­day this ap­proach shouldn’t be too un­wieldy. Start the Ara­bic app and you should now see that the UI is mir­rored – the £ sign should be on the right.

20. Pro­duc­tion

The fi­nal step is to gen­er­ate and check our pro­duc­tion builds. First, though, we need to make an­other quick mod­i­fi­ca­tion to the ‘an­gu­lar.json’ con­fig­u­ra­tion.

In ‘ar­chi­tect.build.con­fig­u­ra­tions’ du­pli­cate the ex­ist­ing pro­duc­tion ob­ject and re­name it ‘”pro­duc­tion-ar-iq”’. Then copy and paste the prop­er­ties from the ex­ist­ing ‘”ar-iq”’ con­fig­u­ra­tion into the ob­ject, so you have both the pro­duc­tion op­tions and the ‘i18n’ op­tions.

You also need to up­date ‘ar­chi­tect.serve.con­fig­u­ra­tions’ too. This time du­pli­cate the ex­ist­ing ‘”ar-iq”’ ob­ject and re­name it ‘”pro­duc­tion-ar-iq”’ and change the ‘browser­tar­get’ value to point to your new ‘pro­duc­tion-ar-iq’ con­fig­u­ra­tion.

Now you can build and serve your pro­duc­tion Ara­bic lo­cale with this com­mand:

$ ng serve --con­fig­u­ra­tion=pro­duc­tion-ar-iq Okay, we’re done! We’ve suc­cess­fully in­ter­na­tion­alised our app, and lo­calised it for ‘en-gb’ and ‘ar-iq’ au­di­ences. An­gu­lar makes the process re­mark­ably straight­for­ward for the de­vel­oper, in fact, the hard­est bit is fig­ur­ing out what the trans­la­tions should be – apolo­gies to any Ara­bic speak­ers if any­thing is wrong!

Newspapers in English

Newspapers from UK

© PressReader. All rights reserved.