Internationalization Support In Angular

Introduction

 
But wait! Does our application really need a translation or conversion to any other languages? Well, the answer is Yes. Because if your application or website is just in English then possibly you are missing out on a big audience.  Below is the chart of languages by the proportion of native speakers.
 
proporation-of-native-speaker-angular 
(image source: Wikipedia )
 
So, any time, if we hear multiple language support, the first thing which comes in our mind is something called localization and internationalization. Right? People often used these two words interchangeably, but actually they are slightly different in meaning.
 

Internationalization

 
Internationalization is the process of designing and preparing your app to be usable in different languages. Localization is the process of translating your internationalized app into specific languages for particular locales.
 

Internationalization deals with the following aspects,

  • Displaying dates, numbers, percentages, and currencies in a local format.
  • Preparing text in component templates for translation.
  • Handling plural forms of words.
  • Handling alternative text.
Whereas Localization actually translates to the actual text.
(Source: angular.io )
 
So now let’s start with an example, I had created the blank new application with angular CLI. And changed my app.component.html as shown below,
  1. <h1 i18n="@@welcome" >Hello World!!</h1>  
  2. <h1 i18n="@@sampletext" >This is just a demo of how to use i18n in angular application. </h1> 
Here I had used i18n on both h1 elements and given two different ids. This will work as a translator text from our translation file. So, we have to use i18n decorator to each and every element of our Html which we want to translate to other languages.
 
We can generate the translation file using angular cli, below is the command.
 
ng xi18n --output-path src\locale
 
In the above command we can specify the path where we actually want to create translation file, below is the how generated file will look like,
 
messages.xlf
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">  
  3.   <file source-language="en" datatype="plaintext" original="ng2.template">  
  4.     <body>  
  5.       <trans-unit id="welcome" datatype="html">  
  6.         <source>Hello World!!</source>  
  7.         <context-group purpose="location">  
  8.           <context context-type="sourcefile">src/app/app.component.html</context>  
  9.           <context context-type="linenumber">1</context>  
  10.         </context-group>  
  11.       </trans-unit>  
  12.       <trans-unit id="sampletext" datatype="html">  
  13.         <source>This is just a demo of how to use i18n in angular application.</source>  
  14.         <context-group purpose="location">  
  15.           <context context-type="sourcefile">src/app/app.component.html</context>  
  16.           <context context-type="linenumber">2</context>  
  17.         </context-group>  
  18.       </trans-unit>  
  19.     </body>  
  20.   </file>  
  21. </xliff> 
Here in the above file as you can see, for each of the ids we set in our component for translation it will generate <trans-unit> tag. So, it has generated separate <trans-unit> tag for both welcome and sample text. So, now we can copy the messages.xlf and create as many translation files or language we want to support. Below is the translation for hindi and gujrati.
 
Messages.hi.xlf
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">  
  3.   <file source-language="en" datatype="plaintext" original="ng2.template">  
  4.     <body>  
  5.       <trans-unit id="welcome" datatype="html">  
  6.         <source>Hello World!!</source>  
  7.         <target>नमस्ते दुनिया!</target>  
  8.         <context-group purpose="location">  
  9.           <context context-type="sourcefile">src/app/app.component.html</context>  
  10.           <context context-type="linenumber">1</context>  
  11.         </context-group>  
  12.       </trans-unit>  
  13.        <trans-unit id="sampletext" datatype="html">  
  14.         <source>This is just a demo of how to use i18n in angular application.</source>  
  15.         <target>यह कोणीय अनुप्रयोग में i18n का उपयोग करने के तरीके का सिर्फ एक डेमो है।</target>  
  16.         <context-group purpose="location">  
  17.           <context context-type="sourcefile">src/app/app.component.html</context>  
  18.           <context context-type="linenumber">2</context>  
  19.         </context-group>  
  20.       </trans-unit>  
  21.     </body>  
  22.   </file>  
  23. </xliff> 
Messages.gu.xlf 
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">  
  3.   <file source-language="en" datatype="plaintext" original="ng2.template">  
  4.     <body>  
  5.       <trans-unit id="welcome" datatype="html">  
  6.         <source>Hello World!!</source>  
  7.         <target>હેલો વર્લ્ડ!</target>  
  8.         <context-group purpose="location">  
  9.           <context context-type="sourcefile">src/app/app.component.html</context>  
  10.           <context context-type="linenumber">1</context>  
  11.         </context-group>  
  12.       </trans-unit>  
  13.       <trans-unit id="sampletext" datatype="html">  
  14.         <source>This is just a demo of how to use i18n in angular application.</source>  
  15.         <target>આ કોણીય એપ્લિકેશનમાં i18n નો ઉપયોગ કેવી રીતે કરવો તેનો ફક્ત એક ડેમો છે.</target>  
  16.         <context-group purpose="location">  
  17.           <context context-type="sourcefile">src/app/app.component.html</context>  
  18.           <context context-type="linenumber">2</context>  
  19.         </context-group>  
  20.       </trans-unit>  
  21.     </body>  
  22.   </file>  
  23. </xliff> 
Lastly, we need to adjust some settings in angular.json file as shown in below,
 
angular.json.file.image
 
Here is the overall angular.json file, 
  1. {  
  2.   "$schema""./node_modules/@angular/cli/lib/config/schema.json",  
  3.   "version": 1,  
  4.   "newProjectRoot""projects",  
  5.   "projects": {  
  6.     "ngInternationalizationDemo": {  
  7.       "projectType""application",  
  8.       "schematics": {},  
  9.       "root""",  
  10.       "sourceRoot""src",  
  11.       "prefix""app",  
  12.       "architect": {  
  13.         "build": {  
  14.           "builder""@angular-devkit/build-angular:browser",  
  15.           "options": {  
  16.             "outputPath""dist/",  
  17.             "index""src/index.html",  
  18.             "main""src/main.ts",  
  19.             "polyfills""src/polyfills.ts",  
  20.             "tsConfig""tsconfig.app.json",  
  21.             "aot"false,  
  22.             "assets": [  
  23.               "src/favicon.ico",  
  24.               "src/assets"  
  25.             ],  
  26.             "styles": [  
  27.               "src/styles.css"  
  28.             ],  
  29.             "scripts": []  
  30.           },  
  31.           "configurations": {  
  32.             "production": {  
  33.               "fileReplacements": [  
  34.                 {  
  35.                   "replace""src/environments/environment.ts",  
  36.                   "with""src/environments/environment.prod.ts"  
  37.                 }  
  38.               ],  
  39.               "optimization"true,  
  40.               "outputHashing""all",  
  41.               "sourceMap"false,  
  42.               "extractCss"true,  
  43.               "namedChunks"false,  
  44.               "aot"true,  
  45.               "extractLicenses"true,  
  46.               "vendorChunk"false,  
  47.               "buildOptimizer"true,  
  48.               "budgets": [  
  49.                 {  
  50.                   "type""initial",  
  51.                   "maximumWarning""2mb",  
  52.                   "maximumError""5mb"  
  53.                 },  
  54.                 {  
  55.                   "type""anyComponentStyle",  
  56.                   "maximumWarning""6kb",  
  57.                   "maximumError""10kb"  
  58.                 }  
  59.               ]  
  60.             },  
  61.             "production-hi": {  
  62.               "fileReplacements": [  
  63.                 {  
  64.                   "replace""src/environments/environment.ts",  
  65.                   "with""src/environments/environment.prod.ts"  
  66.                 }  
  67.               ],  
  68.               "optimization"true,  
  69.               "outputHashing""all",  
  70.               "sourceMap"false,  
  71.               "extractCss"true,  
  72.               "namedChunks"false,  
  73.               "aot"true,  
  74.               "extractLicenses"true,  
  75.               "vendorChunk"false,  
  76.               "buildOptimizer"true,  
  77.               "outputPath""dist/",  
  78.               "i18nFile""src/locale/messages.hi.xlf",  
  79.               "i18nFormat""xlf",  
  80.               "i18nLocale""hi",  
  81.               "i18nMissingTranslation""error"  
  82.             },  
  83.             "hi": {  
  84.               "aot"true,  
  85.               "outputPath""dist/",  
  86.               "i18nFile""src/locale/messages.hi.xlf",  
  87.               "i18nFormat""xlf",  
  88.               "i18nLocale""hi",  
  89.               "i18nMissingTranslation""error"  
  90.             },  
  91.             "production-gu": {  
  92.               "fileReplacements": [  
  93.                 {  
  94.                   "replace""src/environments/environment.ts",  
  95.                   "with""src/environments/environment.prod.ts"  
  96.                 }  
  97.               ],  
  98.               "optimization"true,  
  99.               "outputHashing""all",  
  100.               "sourceMap"false,  
  101.               "extractCss"true,  
  102.               "namedChunks"false,  
  103.               "aot"true,  
  104.               "extractLicenses"true,  
  105.               "vendorChunk"false,  
  106.               "buildOptimizer"true,  
  107.               "outputPath""dist/",  
  108.               "i18nFile""src/locale/messages.gu.xlf",  
  109.               "i18nFormat""xlf",  
  110.               "i18nLocale""gu",  
  111.               "i18nMissingTranslation""error"  
  112.             },  
  113.             "gu": {  
  114.               "aot"true,  
  115.               "outputPath""dist/",  
  116.               "i18nFile""src/locale/messages.gu.xlf",  
  117.               "i18nFormat""xlf",  
  118.               "i18nLocale""gu",  
  119.               "i18nMissingTranslation""error"  
  120.             }  
  121.           }  
  122.         },  
  123.         "serve": {  
  124.           "builder""@angular-devkit/build-angular:dev-server",  
  125.           "options": {  
  126.             "browserTarget""ngInternationalizationDemo:build"  
  127.           },  
  128.           "configurations": {  
  129.             "production": {  
  130.               "browserTarget""ngInternationalizationDemo:build:production"  
  131.             },  
  132.             "hi":{  
  133.               "browserTarget""ngInternationalizationDemo:build:hi"  
  134.             },  
  135.             "gu":{  
  136.               "browserTarget""ngInternationalizationDemo:build:gu"  
  137.             }  
  138.           }  
  139.         },  
  140.         "extract-i18n": {  
  141.           "builder""@angular-devkit/build-angular:extract-i18n",  
  142.           "options": {  
  143.             "browserTarget""ngInternationalizationDemo:build"  
  144.           }  
  145.         },  
  146.         "test": {  
  147.           "builder""@angular-devkit/build-angular:karma",  
  148.           "options": {  
  149.             "main""src/test.ts",  
  150.             "polyfills""src/polyfills.ts",  
  151.             "tsConfig""tsconfig.spec.json",  
  152.             "karmaConfig""karma.conf.js",  
  153.             "assets": [  
  154.               "src/favicon.ico",  
  155.               "src/assets"  
  156.             ],  
  157.             "styles": [  
  158.               "src/styles.css"  
  159.             ],  
  160.             "scripts": []  
  161.           }  
  162.         },  
  163.         "lint": {  
  164.           "builder""@angular-devkit/build-angular:tslint",  
  165.           "options": {  
  166.             "tsConfig": [  
  167.               "tsconfig.app.json",  
  168.               "tsconfig.spec.json",  
  169.               "e2e/tsconfig.json"  
  170.             ],  
  171.             "exclude": [  
  172.               "**/node_modules/**"  
  173.             ]  
  174.           }  
  175.         },  
  176.         "e2e": {  
  177.           "builder""@angular-devkit/build-angular:protractor",  
  178.           "options": {  
  179.             "protractorConfig""e2e/protractor.conf.js",  
  180.             "devServerTarget""ngInternationalizationDemo:serve"  
  181.           },  
  182.           "configurations": {  
  183.             "production": {  
  184.               "devServerTarget""ngInternationalizationDemo:serve:production"  
  185.             }  
  186.           }  
  187.         }  
  188.       }  
  189.     }},  
  190.   "defaultProject""ngInternationalizationDemo"  
Finally, we can run the application using the below command,
 
angular-output-image1
ng serve --configuration=gu
angular-output-image2
ng serve
angular-output-image3
 
Now that is simple, isn’t it? But the demo we had developed will work only for a production environment or only for the local machines. But what if we want to use the same demo in a real application? Because once our application is deployed or live on the server, we just cannot change its configurations. Well, there are certain ways by which we do the above-said task, but my personal choice will be creating separate builds for separate languages, say for example if someone wants to run the Hindi version of my application then the path would be, www.xyz.com/hi  and the same case for other languages also.
 
So before deploying our application to the server let’s add 3 different languages button which we want to support.so modify app.component.html as shown below,
  1. <h1 i18n="@@welcome" >Hello World!!</h1>  
  2. <h1 i18n="@@sampletext" >This is just a demo of how to use i18n in angular application.</h1>  
  3.   
  4. <a href="\">English</a>  
  5. <br>  
  6. <a href="\gu">Gujarati</a>  
  7. <br>  
  8. <a href="\hi">Hindi</a> 
Here in the above html we added 3 anchor tag, and we are simply appending path to its respected directory using href property of anchor tag.
 
That’s it we are all done and now it's time to generate deployment build. We can generate a simple English build, which is a by default language using the following command.
 
ng build --prod
 
This will generate our build deployment build inside the dist directory which we can then publish to our server.
 
So now again we have to generate a separate build for hindi and gujarati languages. We can do it by following commands,
 
ng build --prod --i18n-locale hi --i18n-format xlf --i18n-file src/locale/messages.hi.xlf --output-path=dist/hi --baseHref /hi/
 
So as you can see we are specifying i18n-locale parameter to Hindi (hi), we are also providing the source file location and we are setting output directory where our build should be created to dist/hi and most important parameter baseHref from where we want to serve our local language angular application.
 
We can run the same command for other languages.
 
ng build --prod --i18n-locale gu --i18n-format xlf --i18n-file src/locale/messages.gu.xlf --output-path=dist/gu --baseHref /gu/
 
Now our dist directory should look like this,
 
internationalization-demo 
 
So now we are ready to deploy our application to github pages, but before deploying to github pages we must remove dist directory from .gitignore file.
 

Create the Repository

  • Go to github.com
  • Create the new repository and name it as shown below
GitHubusername.github.io Example: jinalshah999.github.io
 
image1 
 

Copy Remote Server URL

 
It will be required when we will upload the dist folder.
 
remote-server-url-image
 
Now Open the Command Prompt and navigate to the source directory and follow the steps
 
git remote add origin CopiedURL
 
copied-url-image
 
git add 
 
git-add-image
git commit –m “message”
 
git-commit-message-image
 
git subtree push --prefix dist origin master
 
git-subtree-push
 
Output
 
 
output1
 
output2
 
output3
 

Summary

 
In this article, we learned about Internationalization.