Angular i18n & l10n: Extracting Text and Generating Translation Files – A Hilariously Practical Guide
Alright, buckle up, Angular adventurers! 🚀 Today, we’re diving headfirst into the fascinating world of Internationalization (i18n) and Localization (l10n) in Angular. Specifically, we’re tackling the somewhat daunting, yet ultimately rewarding, task of extracting text and generating translation files using the Angular CLI.
Think of your application as a delicious pizza 🍕. You’ve crafted the perfect crust (your core code), slathered on the most amazing sauce (your functionality), and sprinkled it with delightful toppings (your UI). But what if your pizza only speaks English? What about all the hungry Italian, Spanish, or Japanese eaters out there? That’s where i18n and l10n come in! They’re like the universal translator, ensuring everyone can enjoy your culinary creation, no matter their native tongue.
But first, a quick distinction:
- i18n (Internationalization): This is the preparation process. You’re modifying your code to support different languages. Think of it as building the pizza oven so it can bake any kind of pizza, regardless of the ingredients.
- l10n (Localization): This is the adaptation process. You’re translating the text and tailoring the content to specific languages and regions. This is actually making the Italian, Spanish, or Japanese pizza.
In this lecture, we’ll be focusing on the i18n part – preparing our Angular app for localization by extracting all those pesky English strings and getting them ready for translation. So, grab your virtual aprons, and let’s get cooking! 🧑‍🍳
Why Bother with i18n & l10n? (Besides Avoiding a Linguistic Riot)
Before we get our hands dirty, let’s quickly address the elephant in the room: why even bother? Is it really worth the effort? The answer, my friends, is a resounding YES!
Here’s why:
- Reach a Wider Audience: Obvious, right? But it’s HUGE! More languages = more users = more happy customers = more revenue! đź’°đź’°đź’°
- Improve User Experience: People are more comfortable using applications in their native language. It’s like hearing a familiar voice in a crowded room. It builds trust and makes the experience more enjoyable.
- Gain a Competitive Edge: In today’s global marketplace, having a multilingual application can be a significant differentiator.
- Boost SEO: Localized content can improve your search engine rankings in different regions. Google loves multilingual websites! đź’–
Our Culinary Tools (The Angular CLI)
The Angular CLI is our sous chef in this endeavor. It provides a set of commands that make i18n and l10n a whole lot easier. We’ll be using it to:
- Mark text for translation: Identify the strings in our templates and components that need to be translated.
- Extract the marked text: Pull out all those strings into a translation source file.
- Generate translation files: Create empty translation files for each language we want to support.
Step 1: Adding the @angular/localize
Package (The Secret Ingredient)
Before we can start marking text for translation, we need to install the @angular/localize
package. This package provides the necessary tools to translate text at runtime. Think of it as the magic spice blend that makes our international pizza truly authentic.
Open your terminal and run:
ng add @angular/localize
The CLI will automatically update your angular.json
file to include the necessary configurations.
Important Note: If you’re using an older version of Angular (pre-9), you might need to polyfill the $localize
function. Consult the Angular documentation for details.
Step 2: Marking Text for Translation (Sprinkling the i18n Dust)
This is where the real fun begins! We need to tell Angular which text in our application needs to be translated. We do this using the i18n
attribute. Think of it as sprinkling a special "i18n dust" on all the text that needs to be translated.
a) In HTML Templates:
The most common place to mark text is in your HTML templates. Use the i18n
attribute directly on the element containing the text.
<h1>
<span i18n="page title|The title of the main page">Welcome to My Awesome App!</span>
</h1>
<p i18n="greeting|A friendly greeting to the user">Hello, {{username}}! How are you today?</p>
<button i18n="submit button|The text on the submit button">Submit</button>
Explanation:
i18n
attribute: This tells Angular that the text within the element needs to be translated.page title|The title of the main page
: This is the i18n metadata. It consists of two parts, separated by a pipe (|
):page title
(The Meaning): A unique identifier for the text. This is used to link the translated text back to the original text. Choose descriptive and consistent meanings! "ButtonText" is vague. "SubmitButtonText" is better. "CheckoutSubmitButtonText" is even better!The title of the main page
(The Description): A short description of the text. This helps translators understand the context and provide accurate translations. Be as descriptive as possible! A good description can save a translator a lot of time and prevent misinterpretations.
Best Practices for i18n Metadata:
- Be Descriptive: Make the meaning and description as clear and informative as possible. Imagine you’re explaining the text to someone who knows nothing about your application.
- Be Consistent: Use consistent meanings for similar text across your application. This makes it easier to manage translations and avoid duplication.
- Use Meaningful Meanings: Avoid generic meanings like "text1" or "label1". Instead, use descriptive names that reflect the purpose of the text.
- Avoid Special Characters: Stick to alphanumeric characters and underscores in your meanings.
b) In TypeScript Components:
You can also mark text for translation in your TypeScript components. This is useful for translating dynamic text that’s generated in your code.
import { Component } from '@angular/core';
import { $localize } from '@angular/localize/init';
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponentComponent {
greeting: string = $localize`:greeting|A personalized greeting:Hello, World!`;
getWelcomeMessage(): string {
return $localize`:welcome|A welcome message for new users:Welcome to our platform!`;
}
}
Explanation:
$localize
tag: This function marks the string for translation.:greeting|A personalized greeting:Hello, World!
: Similar to the HTML attribute, this is the i18n metadata, consisting of the meaning, description, and the default text.
c) Handling Pluralization and Gender:
i18n gets really interesting when you have to deal with pluralization and gender. Different languages have different rules for pluralization, and some languages have grammatical genders. Angular provides mechanisms to handle these complexities.
Pluralization:
<p i18n="item count|Display the number of items in the cart">
You have {itemCount, plural,
=0 {no items}
=1 {one item}
other {{{itemCount}} items}
} in your cart.
</p>
Explanation:
{itemCount, plural, ...}
: This is a pluralization expression. It tells Angular to choose different text based on the value ofitemCount
.=0 {no items}
: IfitemCount
is 0, display "no items".=1 {one item}
: IfitemCount
is 1, display "one item".other {{{itemCount}} items}
: For all other values ofitemCount
, display the number of items. The double curly braces{{...}}
are used to interpolate the value ofitemCount
.
Gender:
<p i18n="invitation message|A message inviting someone to an event">
You are invited to our event!
{gender, select,
male {He will be attending.}
female {She will be attending.}
other {They will be attending.}
}
</p>
Explanation:
{gender, select, ...}
: This is a gender selection expression. It tells Angular to choose different text based on the value ofgender
.male {He will be attending.}
: Ifgender
is "male", display "He will be attending.".female {She will be attending.}
: Ifgender
is "female", display "She will be attending.".other {They will be attending.}
: For all other values ofgender
, display "They will be attending.".
Important: These pluralization and gender expressions use ICU Message Format, a powerful but somewhat complex syntax. Refer to the ICU documentation for more details.
Step 3: Extracting Translation Units (Harvesting the i18n Crop)
Now that we’ve sprinkled our i18n dust, it’s time to harvest the crop! We need to extract all the marked text into a translation source file. This file will contain all the text that needs to be translated, along with the i18n metadata.
Run the following command in your terminal:
ng extract-i18n --output-path src/locale
Explanation:
ng extract-i18n
: This is the Angular CLI command for extracting i18n text.--output-path src/locale
: This specifies the directory where the translation source file will be saved. In this case, it’ssrc/locale
. You can choose a different directory if you prefer.
This command will generate a file named messages.xlf
(or messages.xtb
if you use the --format xtb
option) in the src/locale
directory.
Understanding the messages.xlf
(or messages.xtb
) File:
This file is in XML Localization Interchange File Format (XLIFF), a standard format for exchanging localization data. It contains all the extracted text, along with the i18n metadata.
Here’s a simplified example of what the messages.xlf
file might look like:
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="page title" datatype="html">
<source>Welcome to My Awesome App!</source>
<note priority="1" from="description">The title of the main page</note>
<note priority="1" from="meaning">page title</note>
</trans-unit>
<trans-unit id="greeting" datatype="html">
<source>Hello, {{username}}! How are you today?</source>
<note priority="1" from="description">A friendly greeting to the user</note>
<note priority="1" from="meaning">greeting</note>
</trans-unit>
<trans-unit id="submit button" datatype="html">
<source>Submit</source>
<note priority="1" from="description">The text on the submit button</note>
<note priority="1" from="meaning">submit button</note>
</trans-unit>
</body>
</file>
</xliff>
Explanation:
<xliff>
: The root element of the XLIFF file.<file>
: Contains information about the source file.source-language="en"
: Specifies the source language (English in this case).<trans-unit>
: Represents a single translation unit (i.e., a piece of text that needs to be translated).id="page title"
: The unique identifier for the translation unit (the "meaning" we specified in thei18n
attribute).<source>
: The original text that needs to be translated.<note>
: Contains the description and meaning of the text.
Step 4: Generating Translation Files (Planting the Seeds of Localization)
Now that we have our translation source file, we can generate empty translation files for each language we want to support. These files will be used to store the translated text.
Example: Generating a translation file for Spanish (es):
-
Copy the
messages.xlf
file: Create a copy of themessages.xlf
file and rename it tomessages.es.xlf
. Place this file in thesrc/locale
directory (or wherever you put the original). -
Modify the
messages.es.xlf
file: Openmessages.es.xlf
and change thetarget-language
attribute in the<file>
element to "es":<file source-language="en" target-language="es" datatype="plaintext" original="ng2.template">
-
Add
<target>
elements: For each<trans-unit>
, add a<target>
element containing the translated text. Initially, you can just copy the<source>
text into the<target>
element. The translators will then replace the placeholder text with the actual translations.<trans-unit id="page title" datatype="html"> <source>Welcome to My Awesome App!</source> <target>¡Bienvenido a mi increĂble aplicaciĂłn!</target> <note priority="1" from="description">The title of the main page</note> <note priority="1" from="meaning">page title</note> </trans-unit>
Important Considerations:
- File Naming Convention: Use the
messages.<locale>.xlf
naming convention for your translation files, where<locale>
is the language code (e.g.,es
for Spanish,fr
for French,de
for German). - Translation Workflow: You’ll typically send these translation files to a professional translation service or a team of translators. They will then fill in the
<target>
elements with the appropriate translations. - Version Control: Make sure to add your translation files to your version control system (e.g., Git) so you can track changes and collaborate with your translation team.
Step 5: Configuring Angular to Use the Translation Files (Baking the International Pizza)
Finally, we need to configure Angular to use the translation files at runtime. This involves modifying your angular.json
file.
Open your angular.json
file and find the projects
section. Within your project, find the architect
section, then the build
and serve
sections. You’ll need to add a configurations
section to both the build
and serve
targets.
"projects": {
"my-app": {
"architect": {
"build": {
"configurations": {
"en": {
"localize": ["en"]
},
"es": {
"localize": ["es"]
}
}
},
"serve": {
"configurations": {
"en": {
"browserTarget": "my-app:build:en"
},
"es": {
"browserTarget": "my-app:build:es"
}
}
}
}
}
}
Explanation:
"en"
and"es"
: These are the names of the configurations. You can choose any names you like, but it’s best to use the language codes for clarity."localize": ["en"]
and"localize": ["es"]
: This tells Angular which language(s) to use for each configuration."browserTarget": "my-app:build:en"
and"browserTarget": "my-app:build:es"
: This tells theserve
command which build configuration to use.
Now, you can build and serve your application with a specific language using the --configuration
flag:
ng build --configuration=es
ng serve --configuration=es
This will build and serve your application with the Spanish translations.
Alternative: Runtime Language Switching
Instead of building separate versions for each language, you can also implement runtime language switching. This allows users to change the language of the application while it’s running. This is a more advanced topic, but it involves using the $localize
API and a language selection mechanism (e.g., a dropdown menu).
Common Pitfalls and How to Avoid Them (Avoiding Culinary Disasters)
- Forgetting to Mark Text: The most common mistake is forgetting to mark text for translation. Double-check your templates and components to make sure you’ve marked all the text that needs to be translated.
- Inconsistent Metadata: Using inconsistent meanings or descriptions can lead to problems with translation management.
- Hardcoded Strings: Avoid hardcoding strings in your code. Instead, use variables or constants that can be easily translated.
- Incorrect Pluralization and Gender Handling: Pluralization and gender handling can be tricky. Make sure you understand the ICU Message Format and test your translations thoroughly.
- Ignoring Context: Provide translators with as much context as possible so they can provide accurate translations.
- Not Testing Translations: Always test your translations to make sure they are accurate and appropriate for the target audience.
Conclusion: Your i18n Pizza is Ready to Serve!
Congratulations! You’ve successfully extracted text and generated translation files using the Angular CLI. Your application is now ready to be localized for different languages and reach a global audience.
Remember, i18n and l10n are ongoing processes. As you add new features and content to your application, you’ll need to continue to mark text for translation and update your translation files. But with the Angular CLI and a little bit of practice, you’ll be able to create truly global applications that delight users around the world!
Now go forth and conquer the world, one language at a time! 🌍 🎉