A simple way to add multiple languages ​​to the site


As part of my project, I was faced with the task of making the company's current website multilingual. More precisely: to create the ability to quickly and easily translate the site into English, Polish, Italian, etc.

An Internet search showed that the existing options for creating a multilingual site are extremely cumbersome and inefficient. Connecting third-party libraries is often problematic, and tips for writing your own solution are associated with a large amount of uniform work.

Writing an alternative method of changing the locale took me only a few hours, and maintaining semantic unity minimizes the changes with the subsequent addition of new pages.

Source files of a sample site with automatic translation can be downloaded on github

Existing Alternatives

When the task first appeared in development, the first step, of course, was the study of ready-made solutions and tips in the forums on how to most easily and correctly implement the possibility of changing the locale. The most popular online resources for creating multilingual websites are offering the following solutions:

  • Creating duplicate html blocks with text in different languages, of which only one is left active for the user, while the rest are hidden (display: none).

    The obvious disadvantage of this method is the incredibly rapid increase in the code and the instantaneous loss of readability and maintainability of the code. In addition, this solution is vulnerable to errors in the text and scalability in terms of increasing the number of languages ​​(hereinafter referred to as locales).
  • Connecting a third-party machine translation service (such as google translate) with a large number of embedded languages ​​and minimal changes in the source code of the page.

    When the task first appeared in the task list, we used this method as the most obvious and convenient, however, experience with clients who are native speakers from the United States and Israel showed that machine translation often makes mistakes when changing locale, and users of sites extremely sharply react to such translation errors. In the end, the strategic partners strongly advised to change the way the locale changed, and this method had to be abandoned.
  • Change language using js features or third-party libraries / frameworks such as jQuery, based on searching and directly modifying DOM elements.

    A feature of this approach is the search for a huge number of js selectors, the text inside which you need to replace. This approach may work well for small projects, but as the number of pages increases, the number of text replacement functions increases proportionally, which leads to a loss of efficiency in large projects.

Alternative solution

The basis of the approach that I propose as an alternative to the existing methods is, oddly enough, the base of the js code I wrote, which is generally trivial, but the rule is the design of selectors, which allows for flexible and simple translation of any number of pages into any language without changes to the code base and redundant data duplication.

In changing the locale with an alternative approach, there are three main players:

  • html page with the established rule for the design of block selectors with text
  • general js service, the main task of which is to replace textContet DOM elements according to the rule for the design of selectors
  • JSON file of locale containing structure with content of html blocks in all languages ​​used when changing locale

Compliance with the design rules for selectors of changeable elements allows you to get rid of the need to change the js code for the locale change service, which is a big plus in terms of the scalability of the project.

The rule of building selectors

Most methods of changing the page locale (among the alternatives 1.3 and partially 2 among them) imply the need to “mark” a variable html block in some way, as correctly by changing the class field. The same mechanism uses an alternative option.

The first step in the design of the selectors is to divide the source page into top-level functional blocks. On our company's page, these are blocks: We
give each block a code name, for example,

  • Menu (menu)

  • Business card (home)

  • Example of the service (example)

  • Partners (clients)

  • Scope of service (userfulBlock)

  • Examples of the service (examples)

  • Contacts and feedback (contacts)

An example on the site
An example on the site

After that, we further divide each block into smaller functional blocks, as is done when using the React library.

An example on the site
An example on the site

We assign our names to the selected areas and get the structure of the form:

  • menu

  • home main, description, buttons

  • example statistics, headline, description, buttons

  • clients buttons

  • userfulBlock headline, userfulCards, elseBlock

  • examples headline, cards

  • contacts headline, description, contacts, form

Further we continue this procedure until we reach the blocks containing the source text.

As a result, we get a ready-made JSON file structure of the locale containing all the necessary texts to change the language. Also, based on this algorithm, the rule for building selectors is determined:

Each selector starts with the keyword locale and further, according to the dash case style, the names of all parent blocks are added, including the block containing the source text, for example, the example description in the first card will have a selector locale-example -cards-description

An example on the site
An example on the site

An example of the resulting json file locale can be seen on github

Service change locale

The locale change service is a module that contains a function to load a locale file.


with optional parameter defLang - language set after loading the locale (default language), as well as the main function of changing the current locale


indicating the required language.

Load locale function

The locale download function uses a standard XMLHttpRequest request for data. The use of this standard standard is due to the desire to minimize the number of dependencies and the ease of use of the request. After receiving the locale file, a notification about data acquisition is displayed in the console, and the function of changing the locale to the default language is called if this language was passed to the function as an optional parameter. You can view the function code here:

functionloadLocale(defLang) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", 'http://localhost:3000/locale.json', true);
    xhr.onreadystatechange = saveLocale.bind(this);
    xhr.onerror = function () { console.log("no found page"); };
    functionsaveLocale() { if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
        locale = JSON.parse(xhr.responseText);
        console.log("locale loaded");
        if(defLang) changeLocale(defLang);
    } }

Locale change function

Data types

It is a recursive function whose main task is to crawl an object containing the page locale (using the DFS algorithm). Using recursion when building a function allows you to code the algorithm as simply and concisely as possible, but too much recursion depth can lead to stack overflow. Features of the workaround for this problem can be found on the forum of the same name, or by reading the relevant articles on habr.com.

The basis of the recursive function is the processing of 4 data types:

  • a field containing a string of source text used to add to the page.
    For example:

    "main": "Продающий квест из вашего видео"

  • a field containing an array of lines of source text used to add to the
    page. Such a field is necessary for creating lists whose elements can change the
    order. For example:


  • A nested data structure containing its own set of fields necessary to build the
    page architecture. For example:

            "home": {
                "main": "selling quest from your video",
                "description": "for social networks & sites",
                "buttons": ["try","order"]

  • An array of nested data structures with the same set of fields used. Such
    arrays are used when lists of identical blocks of code appear, for example,
    cards of team members, or portfolios or tariffs of services provided.
    For example:

            "usefulCards": [
                  "headline": "Marketers and agencies",
                  "statistics": ["convers 26%", "retent 25%"],
                  "button": "ORDER"
                  "headline": "Production studios and TV platforms",
                  "statistics": ["convers 24%", "retent 33%"],
                  "button": "ORDER"
                  "headline": "Conference creators",
                  "statistics": ["convers 65%", "retent 15%"],
                  "button": "ORDER"
                  "headline": "Bloggers and streamers",
                  "statistics": ["convers 24%", "retent 33%"],
                  "button": "ORDER"

    On a website, it might look like this:

    An example on the site
    An example on the site

Processing functions

Processing the data type with the source text is a separate function.

functiongetText(key, object, name,startIndex)

The receiving field is the name of the structure field with the source text, the current locale object containing the text to be added and the current name of the selector needed to find the DOM element.

functiongetText(key, object, name, startIndex) {
    var elementKey=0;
    if(startIndex) elementKey = startIndex;
    for ( ; elementKey < document.getElementsByClassName(name + "-" + key).length; elementKey++)
        if (!isNaN(elementKey)) document.getElementsByClassName(name + "-" + key)[elementKey].textContent = object[key];

An array of strings with source text is also processed by a separate function.

functiongetArrayText(key, object, name,startIndex)

The signature and the body of this function is no different from the past, except that elements from the array are assigned to the DOM elements.

functiongetArrayText(key, object, name, startIndex) {
    var elementKey=0;
    if(startIndex) elementKey = startIndex;
    for ( ; elementKey < document.getElementsByClassName(name + "-" + key).length; elementKey++)
        if (!isNaN(elementKey)) document.getElementsByClassName(name + "-" + key)[elementKey].textContent = object[key][elementKey % object[key].length];

The main recursive replacement function of the text is engaged in the classification of the current locale field into one of the 4 types listed above and the corresponding reaction to the resulting type:

functionchangeText(name, object, startIndex) {
    for (key in object)
        if (Array.isArray(object[key]) && typeof object[key] != 'string' && typeof object[key][0] == 'string') getArrayText(key, object, name);
        elseif (typeof object[key] == "object" ){
            if(isNaN(key)) changeText(name + "-" + key, object[key]);
            else changeText(name, object[key],key);
        else getText(key, object, name, startIndex);

This function accepts the current language locale and the root selector (in this case “locale”) as input. Further, when a nested structure or array of structures is detected, the function will recursively call itself, changing the input parameters accordingly.

The main advantage of the alternative approach is that the service described above does not require any functional changes, and is added as a js file using the locale file you created.


The essence of the approach described above consists in a fixed rule for describing selectors and building a locale file. Because of this, there is a unique opportunity to translate any pages out of the box and reuse the already translated material.

The algorithm for building selectors described above is not mandatory and critical for the operation of the service. The service is flexible for extending and adding new methods and algorithms, as well as for building selector names and the json locale structure. A possible advantage will be saving the locale in the browser’s cookie and changing the locale, depending on the location of the service user.

The source files of the sample site with automatic translation can be downloaded on github .

Also popular now: