Localization in Angular with DevOps and Crowdin
Tags: angular, devops
Published: 2020-04-24 10:37:47

It's not uncommon for projects to require localization at some point. Setting up and using localization over time, including translation management, can become a tedious and time-consuming task if not done properly.

This post will dig into the localization setup needed for Angular and how to integrate Crowdin and DevOps to automate as many boring tasks as possible.

- [DevOps](https://azure.microsoft.com/services/devops/) is Microsofts DevOps services
- [Crowdin](https://crowdin.com/) is an online localization tool to manage translations

### The POC project
The project we want to enable localization for, is a simple Angular 9.1 solution. It contains one component with some sample text.

The gist of [enabling localization in Angular](https://angular.io/guide/i18n) is to `ng add @angular/localize` and utilize the i18n-tag. This enables the Angular tooling to extract and create translation source files.

The `angular.json`-file can then be extended for our project to include our localization setup regarding what locales that should be supported. In combination with this, we can add two npm tasks to simply localization support.

```json
"i18n": {
	"sourceLocale": "en-US",
	"locales": {
	  "da": "src/locale/da/messages.xlf"
	}
  }
```
```json
"scripts": {
  "ng": "ng",
  "build": "ng build --prod --localize",
  "localize": "ng xi18n --output-path src/locale"
}
```

The `localize` tasks builds and extracts any strings in our application that supports localization using the Angular CLI command `xi18n`. It uses our setup from our `angular.json`-file. In this case, it extracts text with the expectation of it being `en-US` and places it in the file `src/locale/messages.xlf`.

The build command with the `--locale` argument builds our application and outputs a compiled application for each locale defined. Metadata such as the `<base>`-tag and `i18nLocale` is automatically applied, which reduces the need to create customized build configurations for each supported locale in most cases - required in previous Angular versions.

![Output builds](https://dev-to-uploads.s3.amazonaws.com/i/9sbf4llcmevqqi2ty4s4.png)

### Crowdin integration
The setup for Crowdin is somewhat straight forward. Within integrations, we can enable `Azure Repos` and setup our branch target and configuration. This creates a `crowdin.yml`-file in our repository with the given translation targets.

![Link](https://dev-to-uploads.s3.amazonaws.com/i/2jfatfh7ujkh8gl8uykx.png)
![Branches](https://dev-to-uploads.s3.amazonaws.com/i/20rjl2g4i895u3qlovgb.png)

```yml
files:
  - source: /src/src/locale/messages.xlf
    translation: /src/src/locale/%two_letters_code%/%original_file_name%
```

At this point, our strings is now ready to be translated in the Crowdin portal.

![Crowdin portal](https://dev-to-uploads.s3.amazonaws.com/i/rewp1jcmucqhr4g8exj1.png)

Crowdin automatically detects when new translations has been created. It then creates a pull-request with the updated translations ready to be merged.

![PR Overview](https://dev-to-uploads.s3.amazonaws.com/i/aopcz7d71lc8lhkx27vt.png)
![PR File](https://dev-to-uploads.s3.amazonaws.com/i/zcjoh9qvhp4ytqmkk8r3.png)

### The result
Our translation is handled by Crowdin and automatic synchronization has been enabled between our repository and Crowdin. To serve our different locales locally, we need to add locale-specific configuration targets in our `angular.json`-file.

```json
"build": {
  "configurations": {
	"da": {
	  "localize": ["da"]
	}
  }
},
"serve": {
  "configurations": {
	"da": {
	  "browserTarget": "loca:build:da"
	}
  }
}
```

We can now start a local instance of our application. The `-c` argument specifies our target configuration. Our port defaults to 4200 for our setup, so to run multiple instances, we need to specify a free port.

```
ng serve -c=da
ng serve --port=4201
```

![Localization result](https://dev-to-uploads.s3.amazonaws.com/i/h05l71thz0yakfzymdny.png)

### Future of Angular localization
A much wanted feature for Angular is to add native support for localization in component/TS code. We're getting closer and closer. According to an issue on GitHub, the support can be expected to land with the release of Angular 10... Fingers crossed - until then, we can add our translation definitions in template code using the `i18n`-tag and apply the same id across to map at compile-time.

The syntax will utilize the $localize command. Support for the command itself is already added with the `ng add @angular/localize` import.

This boils down to a syntax looking like the following with the i18n ID explicitly defined.

``title = $localize`:@@title:Localization demo`;``