JavaRush /Java Blog /Random EN /Implementation of multilingual application

Implementation of multilingual application

Published in the Random EN group
Implementation of multilingual application - 1

Today we will talk about multilingualism. So what is it?

Multilingualism, in other words, internationalization , is part of developing an application that can be adapted for several languages ​​without changing the program logic. Consider the situation: you are creating a web application for a large trading company for residents of a large number of countries in which, for example, languages ​​such as Russian, English, and Spanish are spoken. You need to make it convenient for all users. The idea is this: a Russian-speaking reader should be able to see your data in Russian, an American will be more comfortable reading the material in English, and a Spaniard - in Spanish (unexpectedly, right?) Let's consider several internationalization modelsImplementation of multilingual application - 2 today , and for one of them (which is the most I like it :) Let's look at the implementation in Java. As a guinea pig, today we will have a movie data plate. Let's not get too perverted, so we won't have many speakers. For example, that’s OK. And we will translate the names of the films (directors - for the extras): Implementation of multilingual application - 3

1. Table with translation for each language

The essence of this model: each language has a separate table in the database, which contains all the cells that require translation. The disadvantage of this method is that every time we add a new language, we need to add a new table. That is, let’s imagine that our customer is doing very well, and he is expanding his application to many countries (in fact, languages) of the world. This means you will need to add one tablet per tongue. As a result, we will have a database half or almost entirely consisting of auxiliary translation tables: Implementation of multilingual application - 4 Schematic of the films themselves: Implementation of multilingual application - 5Translation tables:
  • Russian Implementation of multilingual application - 6
  • Spanish Implementation of multilingual application - 7
  • English Implementation of multilingual application - 8

2. One for all

In each table that belongs to a specific model, a field with an identifier for the language plate is added. Accordingly, the database also contains this table with translations. The problem is that one object can correspond to several translations (languages). As a result, there will be duplication of entities, and this greatly confuses and complicates the logic, and this is not good. We look at UML: Implementation of multilingual application - 9 Table movies: Implementation of multilingual application - 10 Table languages: Implementation of multilingual application - 11

3. Column per tongue

A separate translation column is created for each column for each language in the table. The disadvantage of this approach is that, again, if a large number of languages ​​are added, the database structure will need to be changed each time, and this is considered a bad approach. Also imagine how inflated the signs demanding internationalization will be. It may be worth considering a model where the number of supported languages ​​is known in advance, there are not too many of them, and each model should exist in all language variations. UML: Implementation of multilingual application - 12All inclusive table: Implementation of multilingual application - 13

4. External translation

This option is implemented by connecting external tools (Google translate, Bing translate, etc.). It is used if you need to provide information to as many visitors as possible, and there is a lot of this information. So many. In this case, you can decide not to directly store information in the database in all languages, but to dynamically translate it. But it is worth remembering that the quality of machine translation often leaves much to be desired. The option can only be considered as very economical (when there are no resources to translate each publication). A common problem in terms of correct translation is that translators who do not know the language well choose the wrong meaning of a word and confuse the user, forcing him to independently figure out the meaning of what is written on the button. It is also important not only to correctly translate the sentence, but also to bring its meaning to a specific language and nationality. Developers have a lot of problems with genders in some languages. They have to duplicate the phrase in the code depending on the gender of the user, and also take into account that not only nouns have gender, but also adjectives and verbs are inflected differently. There are cases when, having selected a language other than English in the application, along with the words of the selected language, there are still untranslated elements. It’s even worse if several languages ​​are displayed and it turns out to be a kind of Babylon, where everything is mixed up and the user cannot understand the application. For example: https://cloud.google.com/translate/

5. Application level support files

Separate files are created to store translations. It can be one file per tongue or one file per tongue per tablet (fine crushing). This option is often used due to the fact that many texts can be stored in these files, which means that the tables and the database itself will not become bloated. The convenience also lies in the fact that you don’t need to knock on the database for these fields, and in the code the files can be dynamically replaced depending on the requested language. As a result, the file serves as a dictionary for us, in which the key is the language, the value is the text. But we are not limited to the ".properties" format below, and these file formats can vary widely - JSON, XML, etc. The disadvantages are that in this case the normalization of the database is greatly reduced. Also, data integrity no longer depends only on the database, but also on the serialization mechanism. Excellent article on this topic Example of dictionary files with translations: Implementation of multilingual application - 14
  • English Implementation of multilingual application - 15
  • Russian Implementation of multilingual application - 16
  • Spanish Implementation of multilingual application - 17

6. Auxiliary translation table for each table

In my opinion, the most flexible solution. The essence of this approach is to create a separate table for languages. When it is necessary to implement the possibility of translations for the table in question, a link is created with the language table, and the link table contains the language id, element id and columns with translations. It's not as scary as it sounds. This approach allows for fairly flexible extensibility of supported languages. Let's take a closer look. UML: Implementation of multilingual application - 18Table with films: Implementation of multilingual application - 19Table of languages: Implementation of multilingual application - 20Table of translations: Implementation of multilingual application - 21 And, as I said above, let's look at the implementation of one of the options in Java code (as you understand, this will be the last option). There’s nothing like that in the application itself: we’ll go from controllers to dao layers. We will look at the create method - for an example this will suffice. So let's go)) Our essence is a film:
@Builder
@Getter
public class Movie {

   private Long id;

   private String producer;
}
Nothing interesting, just implementing the model of the first table. Controller with dto (Data Transfer Object) converters:
@RestController
@RequiredArgsConstructor
@RequestMapping(path = "/cities")
public class MovieController {

   private final MovieService movieService;

   @PostMapping
   public ResponseEntity<moviedto> create(MovieDTO movieDTO) {
       return new ResponseEntity<>(toDTO(movieService.create(fromDTO(movieDTO), movieDTO.getNameTranslations()), movieDTO.getNameTranslations()), HttpStatus.CREATED);
   }

   private Movie fromDTO(MovieDTO dto) {
       return Movie.builder()
               .id(dto.getId())
               .producer(dto.getProducer())
               .build();
   }

   private MovieDTO toDTO(Movie movie, Map<string, string=""> nameTranslation) {
       return MovieDTO.builder()
               .id(movie.getId())
               .producer(movie.getProducer())
               .nameTranslations(nameTranslation)
               .build();
   }
}
In DTO we pass translations as maps, key is the language abbreviation, value is the translation value (movie name). DTO:
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class MovieDTO {

   @JsonProperty("id")
   private Long id;

   @JsonProperty("name")
   private String producer;

   @JsonProperty("nameTranslations")
   private Map<String, String> nameTranslations;//example = "{'en': 'The Matrix', 'ru' : 'Матрица'}"
}
Here we see the dto class itself, as written above, map for translations, the remaining fields are a display of the Movie model. Let’s move on to the movie service:
public interface MovieService {

   Movie create(Movie movie, Map nameList);
}
Its implementation:
@Service
@RequiredArgsConstructor
public class MovieServiceImpl implements MovieService {

   private final MovieDAO movieDAO;
   private LanguageService languageService;

   @Override
   public Movie create(Movie movie, Map<string, string=""> nameList) {
       movieDAO.create(movie);
       Map<Long, String> map = new HashMap<>();
       nameList.forEach((x, y) -> map.put(languageService.getIdByLangCode(x), y));
       movieDAO.createTranslator(movie.getId(), map);
       return movie;
   }
}
Here we see the use of a relatively third-party LanguageService service to retrieve the language id by its abbreviation. And with this identifier we save our translations (also in the form of a map) into the connection table. Let's look at the DAO:
public interface MovieDAO {

   void create(Movie movie);

   void createTranslator(Long movieId, Map<Long,String> nameTranslations);
}
Implementation:
@RequiredArgsConstructor
@Repository
public class MovieDAOImpl implements MovieDAO {
   private final JdbcTemplate jdbcTemplate;

   private static final String CREATE_MOVIE = "INSERT INTO movies(id, producer) VALUES(?, ?)";

   private static final String CREATE_TRANSLATOR = "INSERT INTO movies_translator(movies_id, language_id, name) VALUES(?, ?, ?)";

   @Override
   public void create(Movie movie) {
       jdbcTemplate.update(CREATE_MOVIE, movie.getId(), movie.getProducer());
   }

   @Override
   public void createTranslator(Long movieId, Map<Long, String> nameTranslations) {
       nameTranslations.forEach((x, y) -> jdbcTemplate.update(CREATE_TRANSLATOR, movieId, x, y));
   }
}
Here we see the preservation of the essence and languages ​​for it (dictionary). And yes, Spring JDBC is used here: I consider it preferable for beginners, since it is more transparent. Let's move on to a “third-party” service. Language service:
public interface LanguageService {

   Long getIdByLangCode(String lang);
}
Implementation:
@Service
@RequiredArgsConstructor
public class LanguageServiceImpl implements LanguageService {
   private final LanguageDAO languageDAO;

   @Override
   public Long getIdByLangCode(String lang) {
       return languageDAO.getIdByLangCode(lang);
   }
}
Nothing special, search by shortened name. DAO:
public interface LanguageDAO {

   Long getIdByLangCode(String lang);
}
Implementation:
@RequiredArgsConstructor
@Repository
public class LanguageDAOImpl implements LanguageDAO {
   private final JdbcTemplate jdbcTemplate;

   private static final String FIND_ID_BY_LANG_CODE = "SELECT id FROM languages WHERE lang_code = ?";

   @Override
   public Long getIdByLangCode(String lang) {
       return jdbcTemplate.queryForObject(FIND_ID_BY_LANG_CODE, Long.class, lang);
   }
}
Structure: Implementation of multilingual application - 23 All the models described above have the right to life. You need to decide which one to use based on the situation. Of course, that’s not all: there are many more different approaches, including using different databases for different languages, using caches, different frameworks, and so on. That's all for me today and... Implementation of multilingual application - 24
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION