JavaRush /Java блог /Random UA /Реалізація мультимовності програми
Константин
36 рівень

Реалізація мультимовності програми

Стаття з групи Random UA
Реалізація мультимовності програми - 1

Сьогодні ми поговоримо про мультимовність. То що це?

Мультиязичность, інакше кажучи, інтернаціоналізація - це частина розробки програми, яка може бути адаптована для кількох мов без перекроювання логіки програми. Розглянемо ситуацію: ви створюєте веб-додаток великої торгової компанії для мешканців великої кількості країн, в яких говорять, наприклад, мовами як російська, англійська, іспанська. Вам потрібно зробити його зручним для всіх користувачів. Задум такий: у російськомовного читача має бути можливість бачити ваші дані російською мовою, американцеві буде зручніше прочитати матеріал англійською, а іспанцю — іспанською (несподівано, так?) Розглянемо сьогодні кілька Реалізація мультимовності програми - 2моделей інтернаціоналізації, і для однієї з них (яка найбільше подобається мені) розглянемо реалізацію на Java. Як піддослідний кролик сьогодні у нас буде табличка з даними про фільми. Не будемо особливо перекручуватися, тому колонок у нас буде небагато. Для прикладу – ОК. І перекладати ми будемо назви фільмів (режисери – так, для масовки): Реалізація мультимовності програми - 3

1. Таблиця з перекладом під кожну мову

Суть цієї моделі: кожна мова має окрема таблиця в БД, у якій містяться всі осередки, потребують перекладу. Недолік цього способу полягає в тому, що кожного разу, коли у нас додається нова мова, необхідно додавати нову таблицю. Тобто уявімо, що у нашого замовника ну дуже добре пішли справи, і він розширює свій додаток для багатьох країн (власне, і мов) світу. Отже, потрібно буде додавати по табличці на мову. У підсумку у нас буде БД, що наполовину або майже повністю складається з допоміжних табличок перекладів: Реалізація мультимовності програми - 4 Схема самих фільмів: Реалізація мультимовності програми - 5Таблиці перекладів:
  • Українська Реалізація мультимовності програми - 6
  • Іспанська Реалізація мультимовності програми - 7
  • Англійська Реалізація мультимовності програми - 8

2. Одна на всіх

У кожній таблиці, яка стосується певної моделі, додається поле з ідентифікатором для таблички мови. Відповідно, у БД є ця табличка з перекладами. Проблема в тому, що одному об'єкту може відповідати кілька перекладів (мов). У результаті відбудеться дублювання сутностей, а це сильно плутає та ускладнює логіку, і це не є добре. Дивимося UML: Реалізація мультимовності програми - 9 Таблиця movies: Реалізація мультимовності програми - 10 Таблиця languages: Реалізація мультимовності програми - 11

3. По колонці на мову

Для кожної колонки кожної мови в таблиці створюється окрема колонка перекладу. Мінус такого підходу в тому, що знову ж таки, якщо додаватиметься велика кількість мов, потрібно буде щоразу змінювати структуру бази даних, а це вважається поганим підходом. Також уявіть, наскільки роздмухуватимуться таблички, які потребують інтернаціоналізації. Можливо, варто задуматися про таку модель, коли заздалегідь відома кількість мов, що підтримуються, їх не надто багато, і кожна модель повинна існувати у всіх мовних варіаціях. UML: Реалізація мультимовності програми - 12Таблиця All inclusive: Реалізація мультимовності програми - 13

4. Зовнішній переклад

Цей варіант реалізується з допомогою підключення зовнішніх коштів (Google translate, Bing translate, etc.). Його застосовують, якщо потрібно надати інформацію якомога більше відвідувачів, і цієї інформації багато. Дуже багато. У такому разі можна ухвалити рішення не зберігати прямо інформацію в БД усіма мовами, а динамічно її перекладати. Але при цьому варто пам'ятати, що якість машинного перекладу найчастіше залишає бажати кращого. Варіант може розглядатись лише як дуже економний (коли немає ресурсів на переклад кожної публікації). Частою проблемою з точки зору правильності перекладу є те, що перекладачі, що погано знають мову, вибирають неправильне значення слова і плутають користувача, змушуючи його самостійно додумувати зміст написаного на кнопці. Ще важливо не тільки правильно перекласти пропозицію, а й підвести його сенс до конкретної мови та національності. У розробників виникає багато проблем через пологи в деяких мовах. Їм доводиться дублювати фразу в коді залежно від статі користувача, а також враховувати, що не тільки іменники мають рід, але й прикметники та дієслова по-різному схиляються. Трапляються випадки, коли вибравши не англійську мову в додатку, поряд зі словами вибраної мови все одно зустрічаються неперекладені елементи. Ще гірше, якщо відображаються кілька мов і виходить своєрідний Вавилон, де все змішалося, і користувач не може розібратися в додатку Наприклад: що не тільки іменники мають рід, а й прикметники та дієслова по-різному схиляються. Трапляються випадки, коли вибравши не англійську мову в додатку, поряд зі словами вибраної мови все одно зустрічаються неперекладені елементи. Ще гірше, якщо відображаються кілька мов і виходить своєрідний Вавилон, де все змішалося, і користувач не може розібратися в додатку Наприклад: що не тільки іменники мають рід, а й прикметники та дієслова по-різному схиляються. Трапляються випадки, коли вибравши не англійську мову в додатку, поряд зі словами вибраної мови все одно зустрічаються неперекладені елементи. Ще гірше, якщо відображаються кілька мов і виходить своєрідний Вавилон, де все змішалося, і користувач не може розібратися в додатку Наприклад:https://cloud.google.com/translate/

5. Допоміжні файли на рівні програми

Створюються окремі файли для зберігання перекладів. Може бути по файлу на мову або файлу на мову на одну табличку (дрібніше дроблення). Варіант часто використовується через те, що в цих файлух можна зберігати багато текстів, а значить, таблиці і сама БД не роздмухуватимуться. Зручність полягає ще в тому, що не потрібно за цими полями стукати в БД, і в коді файли можна динамічно підміняти залежно від мови, яку ви запитали. У результаті файл служить нам словником, у якому ключ — мову, значення — текст. Але ми не обмежені наведеним нижче форматом "properties", а формати цих файлів можуть широко відрізнятися - JSON, XML, etc. Слабкі сторони у цьому, що у разі сильно знижується нормалізація бази даних. Також цілісність даних залежить не тільки від бази даних, а й від механізму серіалізації. Відмінна стаття з цієї теми Приклад файлів-словників з перекладами: Реалізація мультимовності програми - 14
  • Англійська Реалізація мультимовності програми - 15
  • Українська Реалізація мультимовності програми - 16
  • Іспанська Реалізація мультимовності програми - 17

6. Допоміжна таблиця перекладів під кожну таблицю

Як на мене, найгнучкіше рішення. Суть цього підходу полягає у створенні окремої таблиці під мови. Коли необхідно реалізувати можливість перекладів для аналізованої таблиці, створюється зв'язок з таблицею мов, а таблиці зв'язків є id мови, id елемента і колонки з перекладами. Все не таке страшно, як звучить. Даний підхід дозволяє реалізувати досить гнучку розширюваність мов, що підтримуються. Давайте подивимося наочно. UML: Реалізація мультимовності програми - 18Таблиця з фільмами: Реалізація мультимовності програми - 19Таблиця мов: Реалізація мультимовності програми - 20Таблиця перекладів: Реалізація мультимовності програми - 21І, як я говорив вище, давайте розглянемо реалізацію одного з варіантів Java-коду (як ви зрозуміли, це буде останній варіант). У додатку нічого такого: ми пройдемося від контролерів до шарів дао. Розглянемо на методі create — для цього вистачить з головою. Отже поїхали)) Наша сутність - фільм:
@Builder
@Getter
public class Movie {

   private Long id;

   private String producer;
}
Нічого цікавого просто реалізація моделі першої таблиці. Контролер із конвертерами dto (Data Transfer Object):
@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();
   }
}
У DTO ми передаємо переклади як мап, key – скорочення мови, value – значення перекладу (ім'я фільму). 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' : 'Матрица'}"
}
Тут ми бачимо сам клас dto, як і написав вище, map для перекладів, решта полів — відображення моделі Movie Переходимо до сервісу фільмів:
public interface MovieService {

   Movie create(Movie movie, Map nameList);
}
Його реалізація:
@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;
   }
}
Тут бачимо використання щодо стороннього сервісу LanguageService, для витягування id мови, з його скорочення. І вже з цим ідентифікатором ми зберігаємо наші переклади (так само як map) в таблицю зв'язку. Подивимося на DAO:
public interface MovieDAO {

   void create(Movie movie);

   void createTranslator(Long movieId, Map<Long,String> nameTranslations);
}
Реалізація:
@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));
   }
}
Тут ми бачимо збереження сутності та мов до неї (словника). І так, тут використовується Spring JDBC: вважаю його кращим для новачків, оскільки він більш прозорий. Переходимо до "стороннього" сервісу. Сервіс мови:
public interface LanguageService {

   Long getIdByLangCode(String lang);
}
Реалізація:
@Service
@RequiredArgsConstructor
public class LanguageServiceImpl implements LanguageService {
   private final LanguageDAO languageDAO;

   @Override
   public Long getIdByLangCode(String lang) {
       return languageDAO.getIdByLangCode(lang);
   }
}
Нічого особливого, пошук за скороченим ім'ям. DAO:
public interface LanguageDAO {

   Long getIdByLangCode(String lang);
}
Реалізація:
@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);
   }
}
Структура: Реалізація мультимовності програми - 23 Усі описані вище моделі мають право життя. Вирішувати, яку саме використовувати, потрібно виходячи із ситуації. Звичайно, це не все: є ще багато різних підходів, серед яких використання різних БД для різних мов, використання кешів, різних фреймворків і так далі. На цьому у мене сьогодні сьогодні все і… Реалізація мультимовності програми - 24
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ