JavaRush /Blog Java /Random-PL /Wdrożenie aplikacji wielojęzycznej

Wdrożenie aplikacji wielojęzycznej

Opublikowano w grupie Random-PL
Wdrożenie aplikacji wielojęzycznej - 1

Dzisiaj porozmawiamy o wielojęzyczności. Więc co to jest?

Wielojęzyczność, innymi słowy internacjonalizacja , jest częścią tworzenia aplikacji, którą można dostosować do kilku języków bez zmiany logiki programu. Rozważmy sytuację: tworzysz aplikację internetową dla dużej firmy handlowej dla mieszkańców dużej liczby krajów, w których mówi się na przykład językami takimi jak rosyjski, angielski i hiszpański. Musisz sprawić, aby było to wygodne dla wszystkich użytkowników. Pomysł jest taki: czytelnik rosyjskojęzyczny powinien mieć możliwość zapoznania się z Twoimi danymi po rosyjsku, dla Amerykanina wygodniej będzie przeczytać materiał po angielsku, a dla Hiszpana – po hiszpańsku (nieoczekiwanie, prawda?) Wdrożenie aplikacji wielojęzycznej - 2Obecnie rozważamy kilka modeli internacjonalizacji i jeden z nich (który najbardziej mi się podoba :) Przyjrzyjmy się implementacji w Javie. Jako królik doświadczalny będziemy dzisiaj mieli tablicę z danymi filmowymi. Nie bądźmy zbyt wypaczeni, bo nie będziemy mieli wielu mówców. Na przykład, to jest w porządku. A my przetłumaczymy tytuły filmów (reżyserzy - dla statystów): Wdrożenie aplikacji wielojęzycznej - 3

1. Tabela z tłumaczeniem dla każdego języka

Istota tego modelu: każdy język ma w bazie danych osobną tabelę, w której znajdują się wszystkie komórki wymagające tłumaczenia. Wadą tej metody jest to, że za każdym razem, gdy dodajemy nowy język, musimy dodać nową tabelę. Czyli wyobraźmy sobie, że nasz klient radzi sobie bardzo dobrze i rozszerza swoją aplikację na wiele krajów (a właściwie języków) świata. Oznacza to, że będziesz musiał dodać jedną tabletkę na każdy język. W rezultacie będziemy mieli bazę danych składającą się w połowie lub prawie w całości z pomocniczych tabel tłumaczeń: Wdrożenie aplikacji wielojęzycznej - 4 Schemat samych filmów: Wdrożenie aplikacji wielojęzycznej - 5Tabele tłumaczeń:
  • Rosyjski Wdrożenie aplikacji wielojęzycznej - 6
  • hiszpański Wdrożenie aplikacji wielojęzycznej - 7
  • język angielski Wdrożenie aplikacji wielojęzycznej - 8

2. Jeden dla wszystkich

W każdej tabeli należącej do konkretnego modelu dodane jest pole z identyfikatorem płytki językowej. W związku z tym baza danych zawiera również tę tabelę z tłumaczeniami. Problem w tym, że jeden obiekt może odpowiadać kilku tłumaczeniom (językom). W rezultacie nastąpi powielanie bytów, co znacznie dezorientuje i komplikuje logikę, a to nie jest dobre. Przyglądamy się UML: Wdrożenie aplikacji wielojęzycznej - 9 Filmy dotyczące tabel: Wdrożenie aplikacji wielojęzycznej - 10 Języki tabel: Wdrożenie aplikacji wielojęzycznej - 11

3. Kolumna na język

Dla każdej kolumny dla każdego języka w tabeli tworzona jest oddzielna kolumna tłumaczeń. Wadą tego podejścia jest to, że w przypadku dodania dużej liczby języków za każdym razem konieczna będzie zmiana struktury bazy danych, co jest uważane za złe podejście. Wyobraź sobie także, jak nadmuchane będą znaki domagające się internacjonalizacji. Może warto rozważyć model, w którym liczba obsługiwanych języków jest znana z góry, nie jest ich zbyt wiele, a każdy model powinien istnieć we wszystkich odmianach językowych. UML: Wdrożenie aplikacji wielojęzycznej - 12Tabela zawierająca wszystkie informacje: Wdrożenie aplikacji wielojęzycznej - 13

4. Tłumaczenie zewnętrzne

Ta opcja jest realizowana poprzez podłączenie zewnętrznych narzędzi (tłumacz Google, tłumacz Bing itp.). Stosuje się go, jeśli chcesz przekazać informacje jak największej liczbie odwiedzających, a jest ich dużo. Tak wiele. W takim przypadku możesz zdecydować się nie na bezpośrednie przechowywanie informacji w bazie danych we wszystkich językach, ale na ich dynamiczne tłumaczenie. Warto jednak pamiętać, że jakość tłumaczenia maszynowego często pozostawia wiele do życzenia. Opcja ta może być uznana jedynie za bardzo ekonomiczną (w przypadku braku środków na przetłumaczenie każdej publikacji). Częstym problemem z punktu widzenia poprawnego tłumaczenia jest to, że tłumacze nie znający dobrze języka wybierają niewłaściwe znaczenie słowa i wprowadzają w błąd użytkownika, zmuszając go do samodzielnego domyślenia się znaczenia tego, co jest napisane na przycisku. Ważne jest także nie tylko prawidłowe przetłumaczenie zdania, ale także przybliżenie jego znaczenia do konkretnego języka i narodowości. Programiści mają wiele problemów z płciami w niektórych językach. Muszą powielić frazę w kodzie w zależności od płci użytkownika, a także wziąć pod uwagę, że nie tylko rzeczowniki mają płeć, ale także przymiotniki i czasowniki są odmiennie odmieniane. Zdarzają się przypadki, gdy po wybraniu w aplikacji języka innego niż angielski, wraz ze słowami wybranego języka, nadal pozostają nieprzetłumaczone elementy. Jeszcze gorzej, jeśli wyświetlanych jest kilka języków i okazuje się, że jest to swego rodzaju Babilon, w którym wszystko jest pomieszane, a użytkownik nie rozumie aplikacji.Na przykład: https://cloud.google.com/translate/

5. Pliki obsługi poziomu aplikacji

Do przechowywania tłumaczeń tworzone są osobne pliki. Może to być jeden pilnik na język lub jeden pilnik na język na tabletkę (drobne rozdrobnienie). Opcja ta jest często wykorzystywana ze względu na to, że w plikach tych można przechowywać wiele tekstów, co sprawia, że ​​tabele i sama baza danych nie ulegną rozdęciu. Kolejną wygodą jest to, że nie trzeba pukać do bazy danych, aby znaleźć te pola, a pliki w kodzie można dynamicznie zastępować w zależności od żądanego języka. Dzięki temu plik pełni dla nas rolę słownika, w którym kluczem jest język, wartością jest tekst. Ale nie ograniczamy się do poniższego formatu „.properties”, a te formaty plików mogą się znacznie różnić – JSON, XML itp. Wadą jest to, że w tym przypadku normalizacja bazy danych jest znacznie ograniczona. Ponadto integralność danych nie zależy już tylko od bazy danych, ale także od mechanizmu serializacji. Doskonały artykuł na ten temat Przykład plików słownika z tłumaczeniami: Wdrożenie aplikacji wielojęzycznej - 14
  • język angielski Wdrożenie aplikacji wielojęzycznej - 15
  • Rosyjski Wdrożenie aplikacji wielojęzycznej - 16
  • hiszpański Wdrożenie aplikacji wielojęzycznej - 17

6. Pomocnicza tabela tłumaczeniowa dla każdej tabeli

Moim zdaniem najbardziej elastyczne rozwiązanie. Istotą tego podejścia jest utworzenie osobnej tabeli dla języków. W przypadku konieczności wdrożenia możliwości tłumaczeń dla danej tabeli tworzone jest łącze z tabelą języków, a tabela linków zawiera identyfikator języka, identyfikator elementu oraz kolumny z tłumaczeniami. To nie jest tak straszne, jak się wydaje. Takie podejście pozwala na dość elastyczną rozszerzalność obsługiwanych języków. Przyjrzyjmy się bliżej. UML: Wdrożenie aplikacji wielojęzycznej - 18Tabela z filmami: Wdrożenie aplikacji wielojęzycznej - 19Tabela języków: Wdrożenie aplikacji wielojęzycznej - 20Tabela tłumaczeń: Wdrożenie aplikacji wielojęzycznej - 21 I tak jak powiedziałem powyżej, przyjrzyjmy się implementacji jednej z opcji w kodzie Java (jak rozumiesz, będzie to ostatnia opcja). W samej aplikacji nie ma czegoś takiego: przejdziemy od kontrolerów do warstw tao. Przyjrzymy się metodzie tworzenia - na przykład to wystarczy. No to chodźmy)) Naszą esencją jest film:
@Builder
@Getter
public class Movie {

   private Long id;

   private String producer;
}
Nic ciekawego, po prostu implementacja modelu z pierwszej tabeli. Kontroler z konwerterami 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();
   }
}
W DTO tłumaczenia przekazujemy jako mapy, klucz to skrót języka, wartość to wartość tłumaczenia (nazwa filmu). 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' : 'Матрица'}"
}
Tutaj widzimy samą klasę dto jak napisano powyżej, mapę tłumaczeń, pozostałe pola to zobrazowanie modelu Movie.. Przejdźmy do serwisu filmowego:
public interface MovieService {

   Movie create(Movie movie, Map nameList);
}
Jego wdrożenie:
@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;
   }
}
Tutaj widzimy użycie stosunkowo zewnętrznej usługi LanguageService do pobrania identyfikatora języka za pomocą jego skrótu. I za pomocą tego identyfikatora zapisujemy nasze tłumaczenia (również w formie mapy) w tabeli połączeń. Spójrzmy na DAO:
public interface MovieDAO {

   void create(Movie movie);

   void createTranslator(Long movieId, Map<Long,String> nameTranslations);
}
Realizacja:
@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));
   }
}
Tutaj widzimy zachowanie esencji i języków (słownik). I tak, zastosowano tutaj Spring JDBC: uważam go za lepszy dla początkujących, ponieważ jest bardziej przejrzysty. Przejdźmy do usługi „strony trzeciej”. Obsługa językowa:
public interface LanguageService {

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

   @Override
   public Long getIdByLangCode(String lang) {
       return languageDAO.getIdByLangCode(lang);
   }
}
Nic specjalnego, szukaj po skróconej nazwie. DAO:
public interface LanguageDAO {

   Long getIdByLangCode(String lang);
}
Realizacja:
@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);
   }
}
Struktura: Wdrożenie aplikacji wielojęzycznej - 23 Wszystkie opisane powyżej modele mają prawo do życia. Musisz zdecydować, którego użyć w zależności od sytuacji. Oczywiście to nie wszystko: istnieje o wiele więcej różnych podejść, w tym używanie różnych baz danych dla różnych języków, używanie pamięci podręcznych, różnych frameworków i tak dalej. To wszystko dla mnie dzisiaj i... Wdrożenie aplikacji wielojęzycznej - 24
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION