Skip to main content

Angular 12+ multi language

 I wrote a similar post that works for Angular 8, 9, 10, 11.

Click here for the previous post.

In Angular 12+ the config seems to be a bit different.

1. Create an angular web project.

we will start by creating a project with minimal setup to demonstrate.
To create an angular app, in command prompt type:
ng new multiLanguageApp
Open "app.component.html" delete the default code and paste this code:
<div>
  <a href="/">de</a>&nbsp;
  <a href="/en">en</a>
</div>
<div>Danke!</div>
Run "ng serve" to serve your app. Go to localhost:4200 you should see:
I am creating an app with German as the default language and English as the second language.
"Danke" is a word in German means "Thank you". When we click "de" or "en" in the UI to switch language, we expect this word to change respectively.
2. Set up i18n and translate.
In the tag contains texts you want to translate add attribute "i18n". If there is no tag, you can use <ng-container>your text here</ng-container> to wrap your texts. You can read more about i18n attribute in Angular document but I think that's is all we need to know.
In my case I need to change <div>Danke!</div> to <div i18n>Danke!</div> and that's it!.
When you know how to do it with a word you can do it with any texts.
Now for the translation part, we need a few packages.
Add i18n toolings and tell angular that we are using "de" as the default language and we use "de" and "en" languages.
This command is different from the previous post I wrote for older Angular versions. 
Run the command below and it will ask you the default language and languages you want to translate to. 

Which default language do you use in your templates? -> de
Which languages would you like to configure? (comma separated list like en,de,fr)  -> en

This command will also add xliffmerge to help you manage your translation files.
ng add @ngx-i18nsupport/tooling
Open package.json you will see a new script is added
 "extract-i18n""ng xi18n multiLanguageApp --i18n-format xlf --output-path i18n --i18n-locale de && ng run multiLanguageApp:xliffmerge"

This script is used to extract texts for translating. Notice the part --output-path i18n it let you choose the output folder for your files. I like mine to stay inside the src folder so I change it  to --output-path src/i18n

Add @angular/localize, because Angular needs it for this task.
ng add @angular/localize

Now if you run npm run extract-i18n it won't work because some syntax in the script has been deprecated.
Change the script above to:
"extract-i18n""ng extract-i18n multiLanguageApp --format xlf --output-path src/i18n && ng run multiLanguageApp:xliffmerge"

That's it, now run npm run extract-i18n to extract text for translating.
Notice that the command creates 3 files in src/i18n.
messages.xlf is the source file you don't need to care about it.
messages.de.xlf contains texts to translate for German, but since our default language is German so we just leave it as it is.
messages.en.xlf contains texts to translate for English and this is the file we need to translate.

Open messages.en.xlf and look for <target> tag with state="new" (not yet translated), translate texts inside the tag, when you are finished change state to "final" (translated).

This is my file after translated:
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  <file source-language="de" datatype="plaintext" original="ng2.template" 
target-language="en">
    <body>
      <trans-unit id="6911867083572686626" datatype="html">
        <source>Danke!</source><target state="final">Thank you!</target>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/app.component.html</context>
          <context context-type="linenumber">1</context>
        </context-group>
      </trans-unit>
    </body>
  </file>
</xliff>
Next time if you add something new to your app, just run npm run extract-i18n again, it will automatically merge new files and old files so you just need to translate new texts. (texts inside <target> tag with state="new")

Now if you run ng s --configuration=en it won't work, this is the second change compared to my previous post.
In angular.json remove i18nFile, i18nFormat, i18nLocale, these options do not work anymore.
            "en": {
              "aot": true,
              "outputPath": "dist/multiLanguageApp-en",
              "i18nFile": "src/i18n/messages.en.xlf",
              "i18nFormat": "xlf",
              "i18nLocale": "en"
            }

In angular.json, under the project configuration add this:
      "i18n": {
        "sourceLocale": "vi",
        "locales": {
          "en": {
            "translation": "src/i18n/messages.en.xlf",
            "baseHref": "/en/"
          }
        }
      }

In your "en" configuration add "localize": ["en"]
            "en": {
              "aot": true,
              "outputPath": "dist/multiLanguageApp-en",
              "localize": ["en"]
            }

My whole angular.json look like this:
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "multiLanguageApp": {
      "i18n": {
        "sourceLocale": "vi",
        "locales": {
          "en": {
            "translation": "src/i18n/messages.en.xlf",
            "baseHref": "/en/"
          }
        }
      },
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        },
        "@schematics/angular:application": {
          "strict": true
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/multiLanguageApp",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kb",
                  "maximumError": "1mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2kb",
                  "maximumError": "4kb"
                }
              ],
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true
            },
            "en": {
              "aot": true,
              "outputPath": "dist/multiLanguageApp-en",
              "localize": ["en"]
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "browserTarget": "multiLanguageApp:build:production"
            },
            "development": {
              "browserTarget": "multiLanguageApp:build:development"
            },
            "en": {
              "browserTarget": "multiLanguageApp:build:en"
            }
          },
          "defaultConfiguration": "development"
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "multiLanguageApp:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "inlineStyleLanguage": "scss",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          }
        },
        "xliffmerge": {
          "builder": "@ngx-i18nsupport/tooling:xliffmerge",
          "options": {
            "xliffmergeOptions": {
              "i18nFormat": "xlf",
              "srcDir": "src/i18n",
              "genDir": "src/i18n",
              "defaultLanguage": "de",
              "languages": [
                "de",
                "en"
              ]
            }
          }
        }
      }
    }
  },
  "defaultProject": "multiLanguageApp"
}


Now serve your app in English to see if it works ng s --configuration=en

Go to localhost:4200 if things went well you  should see:

Congratulation your app is now fully translated!

The deploy process and web.config file are the same. Please look for it in this post in the  Deploy the website on Windows using IIS secion.





Comments

Popular posts from this blog

How to use Angular RouteReuseStrategy to create snapshots so you can back to previous pages without reloading data

Github of this  exmaple.  There are a lot of practical scenarios when you load a list of items then go to the detail of an item, when you hit the back button you don't want to reload the item list again. Angular provides you a method to do exactly that. With RouteReuseStrategy when you hit the back button it takes you right back to where you were before without reloading anything. Of course, you can choose to do this or not for any page and it super easy to set up. To do this you need to create a class implement RouteReuseStrategy from @angular/router. There are 5 methods you need to implement: shouldDetach, store, shouldAttach, retrieve, shouldReuseRoute. Let's take a closer look at these methods. shouldDetach shouldDetach ( route :  ActivatedRouteSnapshot ):  boolean When you go to the next page, this method will be trigged, it decides whether the current route (a page) should be detached. In other words, Angular takes a snapshot of it and saves it in memory ...

SEO with Angular, add title, meta keywords, a must-do thing for your website

  For those who don't know what SEO is, SEO stands for Search Engine Optimization, it helps people discover your site through search engines like Google, Bing, ... Take a look at the below picture: To let search engines discover your site, you must let search engines know your site, for google you can use Google Search Console  but that's another topic, I assume search engines already know your site. The 3 most important factors are URL, title tag, and meta tag (description, keyword,...). Search engines will look for those 3 things (and many other factors) to compare with the search query and decide whether your site is relevant or not. Url Try to use meaningful URLs. For example, instead of using a URL like https://yoursite.com/products/1 use https://yoursite.com/products/iphone or even better https://yoursite.com/iphone . You can achieve this by configuring your rooting modules. To make slug URLs you can strip accents your product names and join them with a hyphen (-). For e...

Create and deploy multi language (i18n) website with Angular 8+

This guide should work with Angular 8+.  In  this post: 1. Create an angular web project. 2. Set up i18n and translate. 3. Deploy the website on Windows using IIS. In this guide, I am using 2 languages, the setup for more than 2 languages should be similar. You can find the code of this guide in my github . 1. Create an angular web project. To create an angular app, in command  prompt type: ng new multiLanguageApp Open "app.component.html" delete the default code and paste this code : < div >    < a   href = "/" > de </ a > &nbsp;    < a   href = "/en" > en </ a > </ div > < div > Danke! </ div > Run "ng serve" to serve your app. Go to localhost:4200 you should see: I am creating an app with German as the default language and English as the second language. "Danke" is a word in German means "Thank you". When we click "de" or "en" in the UI to switch langu...