📜 ⬆️ ⬇️

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

Introduction


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 changes with the subsequent addition of new pages.

The source files of the 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 on the forums on how to most easily and correctly implement the possibility of changing the locale. The most popular Internet resources for creating multilingual websites are offering the following solutions:


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 changes to the code base and unnecessary data duplication.

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


Compliance with the design rules for selectable elements allows you to eliminate the need to change the js code of 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) imply the need to “mark” the 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 page are blocks:
Each block is given a code name, for example,


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:


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 locale keyword 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 the locale-example-cards-description selector

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 the function to load a locale file.

loadLocale(defLang) 

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

 changeLocale(lang) 

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 query. 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:

 function loadLocale(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"); }; xhr.send(); function saveLocale() { 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:


Processing functions


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

 function getText(key, object, name,startIndex) 

The host field name of the structure with the source text, the current locale object containing the text that you want to add and the current name of the selector needed to find the DOM element.

 function getText(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]; } 

Processing the array of strings with source text is also performed by a separate function.

 function getArrayText(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.

 function getArrayText(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:

 function changeText(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); else if (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.

Conclusion


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 .

Source: https://habr.com/ru/post/437124/