Software localization

Node.js Tutorial on Creating a Multilingual Web App

Learn how to create a multilingual web app the easy way – our step-by-step Node.js tutorial will show you all the details on i18n in Node.js.
Software localization blog category featured image | Phrase

Building on our Ultimate Guide to Node.js Internationalization, this Node.js tutorial will show you how to create a multilingual web application in Node.js, without the need to install any external localization or internationalization library. Language files will be stored in the JSON data format, which is convertible into JavaScript objects. On top of that, you can easily extend or scale the web application to include the libraries of your choice later on. Let's proceed to the next section and start with setting up the project.

🔗 Resource » Learn everything you need to make your JS apps accessible for users around the globe in our Ultimate Guide on JavaScript Localization.

Setup

Before you get it started, check out the GitHub repository for the complete code for this tutorial. Feel free to clone and run it directly on your local machine as long as you have Node installed.

Node.js

Make sure that you have Node.js installed locally on your machine. You can check it via the following command in the terminal:

node --version

Change the working directory to a root directory of your project. If you are starting from a new project, kindly run the following code to initialize the package setting.

npm init

Fill in the required information. You can skip some of the setting by leaving it blank. After that, it will create a file called package.json based on the information that you have entered.

{

  "name": "multilingual_nodejs",

  "version": "1.0.0",

  "description": "",

  "main": "index.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "author": "Ng Wai Foong",

  "license": "ISC"

}

Glob

No other external module is required except for glob, which is a library to match files using the patterns the shell uses, such as stars and stuff.

For your information, this module is extremely useful as it helps to load all the language files dynamically. It saves you the trouble of having to load a language file manually when you add a new language. Run the following command in the terminal:

npm install glob

It will create a new folder called node_modules containing following files:

node_modules folder | Phrase

Language Files

In the same directory, create a new folder called language. We are going to place all the language files inside this folder. Each of the files is in the JSON format, and their names are based on standardized language code. For this tutorial, I am going to create just two language files for English and German. Create a new file called en.json and append the following data inside it.

{

  "pricing_table": "Pricing Tables",

  "basic": "Basic",

  "pro": "Pro",

  "premium": "Premium",

  "storage": "Storage",

  "email": "Emails",

  "domain": "Domains",

  "sign_up": "Sign Up",

  "cost_basic": "$ 10",

  "cost_pro": "$ 25",

  "cost_premium": "$ 50",

  "per_month": "per month"

}

Repeat the same process for de.json with the following data:

{

  "pricing_table": "Preistabellen",

  "basic": "Basic",

  "pro": "Profi",

  "premium": "Prämie",

  "storage": "Lager",

  "email": "E-Mails",

  "domain": "Domänen",

  "sign_up": "Anmelden",

  "cost_basic": "€ 9",

  "cost_pro": "€ 22.5",

  "cost_premium": "€ 45",

  "per_month": "pro Monat"

}

In the next section, we are going to implement an HTML file for front-end UI.

HTML

To keep things simple, I am going to use a readily available HTML template from the W3C website. The interface looks like this.

HTML template from W3C | Phrase

However, I am going to make some modifications to it, especially on the text data, instead of writing the text normally like this:

<div class="w3-container">

  <h2>Responsive Pricing Tables</h2>

</div>

I am using the key/identifier that I have previously defined in the JSON file and wrapped it inside double curly brackets as follows:

<div class="w3-container">

  <h2>{{pricing_table}}</h2>

</div>

The purpose of this is to allow for smoother post-processing of the HTML file in the Node.js server before serving it to users.

Make sure that you are in the root directory. Create a new file called index.html and append the following code inside the file:

<!DOCTYPE html>

<html>

<title>Phrase Multilingual Web Application in Node.js</title>

<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">

<body>

<br>

<div class="w3-container">

  <h2>{{pricing_table}}</h2>

</div>

<div class="w3-row-padding">

<div class="w3-third w3-margin-bottom">

  <ul class="w3-ul w3-border w3-center w3-hover-shadow">

    <li class="w3-black w3-xlarge w3-padding-32">{{basic}}</li>

    <li class="w3-padding-16"><b>10GB</b> {{storage}}</li>

    <li class="w3-padding-16"><b>10</b> {{email}}</li>

    <li class="w3-padding-16"><b>10</b> {{domain}}</li>

    <li class="w3-padding-16">

      <h2 class="w3-wide">{{cost_basic}}</h2>

      <span class="w3-opacity">{{per_month}}</span>

    </li>

    <li class="w3-light-grey w3-padding-24">

      <button class="w3-button w3-green w3-padding-large">{{sign_up}}</button>

    </li>

  </ul>

</div>

<div class="w3-third w3-margin-bottom">

  <ul class="w3-ul w3-border w3-center w3-hover-shadow">

    <li class="w3-green w3-xlarge w3-padding-32">{{pro}}</li>

    <li class="w3-padding-16"><b>25GB</b> {{storage}}</li>

    <li class="w3-padding-16"><b>25</b> {{email}}</li>

    <li class="w3-padding-16"><b>25</b> {{domain}}</li>

    <li class="w3-padding-16">

      <h2 class="w3-wide">{{cost_pro}}</h2>

      <span class="w3-opacity">{{per_month}}</span>

    </li>

    <li class="w3-light-grey w3-padding-24">

      <button class="w3-button w3-green w3-padding-large">{{sign_up}}</button>

    </li>

  </ul>

</div>

<div class="w3-third w3-margin-bottom">

  <ul class="w3-ul w3-border w3-center w3-hover-shadow">

    <li class="w3-black w3-xlarge w3-padding-32">{{premium}}</li>

    <li class="w3-padding-16"><b>50GB</b> {{storage}}</li>

    <li class="w3-padding-16"><b>50</b> {{email}}</li>

    <li class="w3-padding-16"><b>50</b> {{domain}}</li>

    <li class="w3-padding-16">

      <h2 class="w3-wide">{{cost_premium}}</h2>

      <span class="w3-opacity">{{per_month}}</span>

    </li>

    <li class="w3-light-grey w3-padding-24">

      <button class="w3-button w3-green w3-padding-large">{{sign_up}}</button>

    </li>

  </ul>

</div>

</div>

</body>

</html>

Node.js Server

In this section, we are going to create the Node.js server for the back-end. In the same directory as index.html, create a new JavaScript file called index.js.

Import

Add the following import statement at the top of the file and initialize an empty object called language_dict. Later on, we are going to load the JSON data as JavaScript object and assign it to this instance.

var fs = require('fs');

var http = require('http');

var url = require('url');

var glob = require( 'glob' );

var language_dict = {};

Glob

Next, we are going to use the power of the glob module to search for all the JSON files in the language folder. Once we have obtained the path, we can make use of the fs module to read and parse it as JavaScript object.

glob.sync( './language/*.json' ).forEach( function( file ) {

  let dash = file.split("/");

  if(dash.length == 3) {

  	let dot = dash[2].split(".");

    if(dot.length == 2) {

      let lang = dot[0];

      fs.readFile(file, function(err, data) {

        language_dict[lang] = JSON.parse(data.toString());

      });

    }

  }

});

Server and URL

We are going to call the create server function and use the url module to obtain the URL entered by the users. This helps us to identify the language that has been specified by the users. Continue adding the following code inside the file:

http.createServer(function (req, res) {

  var q = url.parse(req.url, true);

  var lang = 'en';

  let dash = q.pathname.split("/");

  if(dash.length >= 2) {

    let code = dash[1];

    if(code !== '' && language_dict.hasOwnProperty(code)) {

      lang = code;

    }

  }

  //reserved for fileRead

  res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});

  res.write(lang);

  return res.end();

  });

}).listen(8080);

You can run the server via the following command:

node index.js

Open a browser and enter the following URL:

http://localhost:8080/en

You should see the word en. Try again the same with the following:

http://localhost:8080/de

So far, our server only supports two languages. If you use languages that are not supported, it will default to English.

FileRead and Regex

A little modification is required in order to serve the HTML file to the users. We can do so with the URL module. Replace the following lines ...

//reserved for fileRead

  res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});

  res.write(lang);

  return res.end();

... with the following code:

fs.readFile('index.html', function(err, data) {

    res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});

    let data_string = data.toString()

    for (var key of Object.keys(language_dict[lang])) {

      let pattern = new RegExp("{{" + key + "}}", "g");

      data_string = data_string.replace(pattern, language_dict[lang][key]);

    }

    res.write(data_string);

    return res.end();

  });

This code snippet helps to read the HTML file as a string. Then, we use Regex to replace all instances with the corresponding value based on the key/identifiers available. The final code for index.js looks like this:

var fs = require('fs');

var http = require('http');

var url = require('url');

var glob = require( 'glob' );

var language_dict = {};

glob.sync( './language/*.json' ).forEach( function( file ) {

  let dash = file.split("/");

  if(dash.length == 3) {

  	let dot = dash[2].split(".");

    if(dot.length == 2) {

      let lang = dot[0];

      fs.readFile(file, function(err, data) {

        language_dict[lang] = JSON.parse(data.toString());

      });

    }

  }

});

http.createServer(function (req, res) {

  var q = url.parse(req.url, true);

  var lang = 'en';

  let dash = q.pathname.split("/");

  if(dash.length >= 2) {

    let code = dash[1];

    if(code !== '' && language_dict.hasOwnProperty(code)) {

      lang = code;

    }

  }

  fs.readFile('index.html', function(err, data) {

    res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});

    let data_string = data.toString()

    for (var key of Object.keys(language_dict[lang])) {

      let pattern = new RegExp("{{" + key + "}}", "g");

      data_string = data_string.replace(pattern, language_dict[lang][key]);

    }

    res.write(data_string);

    return res.end();

  });

}).listen(8080);

Re-run the server and open the following URL in your browser.

http://localhost:8080/en

You should see the following output

Let's try it now in German via the following URL:

http://localhost:8080/de

Your browser will show the following output:

Localized app | Phrase

Conclusion

Congratulations on completing this Node.s tutorial on i18n. You should be able to create your own customized web application entirely in Node.js. And if you want to streamline your i18n process, sign up for Phrase, the most reliable localization and translation management platform that comes with a 14-day free trial.

Phrase will equip you with everything you need to:

  • Build production-ready integrations with your development workflow,
  • Invite as many users as you wish to collaborate on your projects,
  • Edit and convert localization files online with more context for higher translation quality.

Finally, if you want to learn even more about internationalization in similar frameworks or JavaScript in general, make sure you check out the following articles: