One way for companies to reach new clients is to speak their language. To do that, developers need to use internationalization and localization in their applications to offer products and content in users’ native languages.
Internationalization, or i18n (18 is the number of letters between i and n), is the process of building your product to support multiple languages. This can include separating your text from your code and using a library to format your dates based on different countries and time zones. Once your product is ready to add support for specific languages, you can move to localization.
Localization, or l10n, is the process of adding support for a specific region, country, or language. This is different from translating text into another language, though localization can include translation. Here are some things to keep in mind when localizing a product:
Images also must be adapted to a particular market, especially those displaying text.
This article will show how three major frontend libraries handle localization and how you can use them to create multilingual applications. You can see the code shown here on GitHub.
Keep in mind that a headless CMS can help you achieve localization easily. Strapi, the leading open-source headless CMS with a 135,000-plus community of users, offers customizable solutions for managing and localizing your content.
In less than an hour, you can use Strapi to have API endpoints and an admin panel ready to go. With GraphQL or Rest, you can consume any Strapi API endpoints from any client (Vue, React or Angular, for instance), which gives you great flexibility.
Created by Google in 2017, Flutter is a library that is quickly gaining traction. As expected of a global company like Google, internationalization is part of the library and can be implemented almost instantly.
Flutter supports not only translated text but also plurals, number-and-date formatting, and right-to-left or left-to-right text. This makes it a solid choice for developers.
To start, update your pubspec.yaml
. Add generate true
to automatically generate the .dart
files necessary for every locale that you will add.
1 # ...
2 dependencies:
3 flutter:
4 sdk: flutter
5 flutter_localizations: //Add this
6 sdk: flutter. // this
7 intl: ^0.17.0. // this
8
9 flutter:
10 generate: true // and finally, this
11 uses-material-design: true
12
13 # ...
Run flutter pub get
to get the necessary packages.
Create a l10n.yaml
file in your root directory. This tells Flutter where to find your translations and where to generate the dart files.
1 arb-dir: lib/l10n
2 template-arb-file: app_en.arb
3 output-localization-file: app_localizations.dart
Then create an I10n directory in your lib folder and create your translation files. Here is an example of an app_en.arb
file:
1 {
2 "appTitle": "Home Page"
3 }
In your main.dart
file, import the flutter_localizations
dart package and add the localizations delegates and the supported languages. I used English and French here, but of course you can add your own.
1 import 'package:flutter/material.dart';
2 import 'package:flutter_localizations/flutter_localizations.dart'; // New import
3
4 return MaterialApp(
5 title: 'Flutter Demo App',
6 // The Material, Cupertino packages and widgets will now be correctly localized
7 localizationsDelegates: const [
8 GlobalMaterialLocalizations.delegate,
9 GlobalWidgetsLocalizations.delegate,
10 GlobalCupertinoLocalizations.delegate,
11 ],
12 supportedLocales: const [
13 Locale('en', ''), // English
14 Locale('fr', ''), // French
15 ],
16 theme: ThemeData(
17 primarySwatch: Colors.blue,
18 ),
19 home: MyHomePage(),
20 );
Run the app with flutter run
. You should see these files in your .dart-tool:
.dart_tool/flutter_gen/gen_l10n/app_localizations.dart
.dart_tool/flutter_gen/gen_l10n/app_localizations_en.dart
.dart_tool/flutter_gen/gen_l10n/app_localizations_fr.dart
Now let’s add our localized message.
1 import 'package:flutter/material.dart';
2 import 'package:flutter_localizations/flutter_localizations.dart';
3 //Our newly generated gen_l10n file
4 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
5
6 return MaterialApp(
7 title: 'Localizations Sample App',
8 localizationsDelegates: const [
9 AppLocalizations.delegate, // New delegate
10 GlobalMaterialLocalizations.delegate,
11 GlobalWidgetsLocalizations.delegate,
12 GlobalCupertinoLocalizations.delegate,
13 ],
14 supportedLocales: const [
15 Locale('en', ''),
16 Locale('fr', ''),
17 ],
18 theme: ThemeData(
19 primarySwatch: Colors.blue,
20 ),
21 home: MyHomePage(),
22 );
You can now access your translations through AppLocalizations. You could, for example, pass in a title to your HomePage like this:
1 MyHomePage(title: AppLocalizations.of(context)!.appTitle)
The internationalization package has few limitations, supporting many necessary features such as the handling of plurals or bidirectional text. Being a very new language, though, Flutter doesn’t possess the wealth of third-party packages offered with Ionic or React. Additionally, the bundle size is typically larger than 4 MB.
Older than Flutter, Ionic was created in 2013 and is a solid library offering developers the ability to have one codebase for any platform. Ionic offers support for many frameworks including Angular, Vue, and even React. I will focus on Angular here, as React will be covered below.
While Angular has a built-in internationalization module, the setup is harder for Ionic applications. As a result, two third-party libraries have emerged:
While transloco is newer and offers features like SSR support, ngx-translate is a solid, reliable library that has been around longer and is loved by Angular developers. We’ll use ngx-translate as our translation library here.
To start, you will need to install the necessary library.
npm install @ngx-translate/core @ngx-translate/http-loader --save
In your src/app/assets
, add an i18n folder with your translations. For example, here is a en.json
file:
1 {
2 "title": "Welcome",
3 "description": "This is an Ionic app translated by ngx-translate"
4 }
Head to your app.module.ts
and add your modules (TranslateModule, TranslateLoader, etc.). This will tell your application where your translations are located and how to load them.
1 import { NgModule } from '@angular/core';
2 import { BrowserModule } from '@angular/platform-browser';
3 import { RouteReuseStrategy } from '@angular/router';
4 import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
5 import { TranslateHttpLoader } from '@ngx-translate/http-loader';
6 import { HttpClientModule, HttpClient } from '@angular/common/http';
7 import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
8 import { AppComponent } from './app.component';
9 import { AppRoutingModule } from './app-routing.module';
10
11 /* New function to load our translation files*/
12 export function HttpLoaderFactory(http: HttpClient) {
13 return new TranslateHttpLoader(http, "./assets/i18n/", ".json");
14 }
15 /* Add HttpClientModule & TranslateModule to our imports */
16 @NgModule({
17 declarations: [AppComponent],
18 entryComponents: [],
19 imports: [HttpClientModule, BrowserModule,
20 TranslateModule.forRoot({
21 loader: {
22 provide: TranslateLoader,
23 useFactory: HttpLoaderFactory,
24 deps: [HttpClient]
25 }
26 }),
27 , IonicModule.forRoot(), AppRoutingModule],
28 providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
29 bootstrap: [AppComponent],
30 })
31 export class AppModule {}
In app.component.ts
, set your default language.
1 import { Component } from '@angular/core';
2 import { TranslateService } from '@ngx-translate/core';
3 @Component({
4 selector: 'app-root',
5 templateUrl: 'app.component.html',
6 styleUrls: ['app.component.scss'],
7 })
8 export class AppComponent {
9 constructor(public translate: TranslateService) {
10 this.initializeApp();
11 }
12 initializeApp() {
13 this.translate.addLangs(['en', 'fr']);
14 this.translate.setDefaultLang('en');
15 }
16 }
Finally, try displaying some translated text.
1 <div id="container">
2 <strong>{{ 'title' | translate }} </strong>
3 <p>{{ 'description' | translate }}</p>
4 </div>
There are specific aspects of Ionic that require some workarounds.
For lazy-loaded modules, you will need to import translation modules there as well; otherwise, the translation will not work. Don’t forget to use forChild
instead of forRoot
.
1 import { NgModule } from '@angular/core';
2 import { CommonModule } from '@angular/common';
3 import { IonicModule } from '@ionic/angular';
4 import { FormsModule } from '@angular/forms';
5 import { HomePage } from './home.page';
6 import { HomePageRoutingModule } from './home-routing.module';
7 import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
8 import { HttpClient } from '@angular/common/http';
9 import { TranslateHttpLoader } from '@ngx-translate/http-loader';
10
11 /* Once again, load your translations*/
12 export function HttpLoaderFactory(http: HttpClient) {
13 return new TranslateHttpLoader(http, "./assets/i18n/", ".json");
14 }
15
16 /* Add the translation module again, but this time, with forChild() */
17 @NgModule({
18 imports: [
19 TranslateModule.forChild({
20 loader: {
21 provide: TranslateLoader,
22 useFactory: HttpLoaderFactory,
23 deps: [HttpClient]
24 }
25 }),
26 CommonModule,
27 FormsModule,
28 IonicModule,
29 HomePageRoutingModule
30 ],
31 declarations: [HomePage]
32 })
33 export class HomePageModule {}
Pluralization and gender formatting aren’t included with ngx-translate. However, there is a plugin to handle these features, and it’s recognized by the official ngx-translate library.
React needs little introduction. Created by Facebook in 2013, it quickly became a fan favorite for many frontend developers.
Two major libraries are available for internationalization in React:
While both are popular (12,000 and 6,000 GitHub stars, respectively), react-i18next seems to have won developers over. This library has the added benefit of belonging to the i18next ecosystem, a translation framework offering support to React, React Native, and Electron, among others. Developers can learn it once and easily translate it into many different frameworks.
To use react-i18next, first install the library:
npm install react-i18next i18next --save
In your src folder, beside your index.js
, create an i18n.js
file where you will add your translations and connect react-i18next to React.
1 import i18n from "i18next";
2 import { initReactI18next } from "react-i18next";
3 // Pro tip: Move them into their own JSON files
4 const resources = {
5 en: {
6 translation: {
7 "welcome_message": "Hello and Welcome to React"
8 }
9 },
10 fr: {
11 translation: {
12 "welcome_message": "Bonjour et bienvenue à React"
13 }
14 }
15 };
16 i18n
17 .use(initReactI18next) // Connect react-i18next to React
18 .init({
19 resources,
20 lng: "en", // default language
21 interpolation: {
22 escapeValue: false // react already safe from xss
23 }
24 });
25 export default i18n;
Then, in your index.js
, import your newly created i18n.js
file:
1 import React from 'react';
2 import ReactDOM from 'react-dom';
3 import './index.css';
4 import './i18n';
5 import App from './App';
6
7 ReactDOM.render(
8 <React.StrictMode>
9 <App />
10 </React.StrictMode>,
11 document.getElementById('root')
12 );
You can access your translation through, for example, the useTranslation
hook.
1 import logo from './logo.svg';
2 import './App.css';
3 import { useTranslation } from 'react-i18next';
4 function App() {
5 const { t } = useTranslation();
6 return (
7 <div className="App">
8 <header className="App-header">
9 <img src={logo} className="App-logo" alt="logo" />
10 <p>
11 {t('welcome_message')}
12 </p>
13 </header>
14 </div>
15 );
16 }
17 export default App;
The library is comprehensive and covers many necessary features. Plurals, interpolation, formatting, nesting, and more are handled by react-i18next.
The only thing that gets a bit tricky is translating text with HTML. For example, “Hello, <i>{{name}}</i>! Go to your <Link to=”/inbox”>inbox</Link> to see your new messages”
.
React-i18next handles this use case by transforming your string into a tree node and using replacement tags.
Your string would then be split up:
1Trans.children = [
2'Hello, ', // 0: a string
3{ name: ‘Marie’ }, // 1: <strong> with interpolation
4‘! Go to your ’, // 2: a string
5{ children: ['inbox'] }, // 3: <Link> with a string child
6' to see your new messages' // 4: another string
7]
In your translation files, you would have Hello, <1>{{name}}</1>! Go to your <3>inbox</3> to see your new messages
. The mental gymnastics of figuring out the right index can be confusing.
Users are far more likely to interact with products in their own language, so offering support for more languages and regions could bring you users that your competitors can’t access. If you internationalize your product early, you’ll be better able to add support for other locales as you scale up.
You can cut down on development time with Strapi. With its internationalization plugin, you can create different content versions for each language and country in an easy-to-use editor. All your content is available through API endpoints, allowing you to easily connect your frontend. Whether you’re developing for the web or for mobile, Strapi can help you with your localization process.
Marie Starck is a full-stack software developer. She loves frontend technologies such as React. In her free time, she also writes tech tutorials. if she could, she would get paid in chocolate bars.