JavaRush /Java Blog /Random-IT /Implementazione dell'applicazione multilingue

Implementazione dell'applicazione multilingue

Pubblicato nel gruppo Random-IT
Implementazione dell'applicazione multilingue - 1

Oggi parleremo di multilinguismo. Quindi, cos'è?

Il multilinguismo, in altre parole, l'internazionalizzazione , fa parte dello sviluppo di un'applicazione che possa essere adattata a più lingue senza modificare la logica del programma. Considera la situazione: stai creando un'applicazione web per una grande società commerciale per residenti in un gran numero di paesi in cui, ad esempio, si parlano lingue come russo, inglese e spagnolo. È necessario renderlo conveniente per tutti gli utenti. L'idea è questa: un lettore di lingua russa dovrebbe essere in grado di vedere i tuoi dati in russo, un americano sarà più a suo agio nel leggere il materiale in inglese e uno spagnolo - in spagnolo (inaspettatamente, giusto?). Consideriamo Implementazione dell'applicazione multilingue - 2oggi diversi modelli di internazionalizzazione , e per uno di loro (che è quello che mi piace di più :) Diamo un'occhiata all'implementazione in Java. Come cavia, oggi avremo la targa dati del film. Non diventiamo troppo perversi, così non avremo molti oratori. Ad esempio, va bene. E tradurremo i nomi dei film (registi - per le comparse): Implementazione dell'applicazione multilingue - 3

1. Tabella con traduzione per ogni lingua

L'essenza di questo modello: ogni lingua ha una tabella separata nel database, che contiene tutte le celle che richiedono la traduzione. Lo svantaggio di questo metodo è che ogni volta che aggiungiamo una nuova lingua, dobbiamo aggiungere una nuova tabella. Immaginiamo cioè che il nostro cliente se la passi molto bene e stia espandendo la sua applicazione in molti paesi (anzi, lingue) del mondo. Ciò significa che dovrai aggiungere una compressa per lingua. Avremo così un database composto per metà o quasi interamente da tabelle di traduzione ausiliarie: Implementazione dell'applicazione multilingue - 4 Schema dei film stessi: Implementazione dell'applicazione multilingue - 5Tabelle di traduzione:
  • russo Implementazione dell'applicazione multilingue - 6
  • spagnolo Implementazione dell'applicazione multilingue - 7
  • Inglese Implementazione dell'applicazione multilingue - 8

2. Uno per tutti

In ogni tabella che appartiene ad un modello specifico viene aggiunto un campo con un identificatore per la targa della lingua. Di conseguenza, il database contiene anche questa tabella con le traduzioni. Il problema è che un oggetto può corrispondere a più traduzioni (lingue). Di conseguenza, ci sarà una duplicazione delle entità, e questo confonde e complica notevolmente la logica, e questo non va bene. Consideriamo UML: Implementazione dell'applicazione multilingue - 9 Filmati tabellari: Implementazione dell'applicazione multilingue - 10 Linguaggi tabellari: Implementazione dell'applicazione multilingue - 11

3. Colonna per lingua

Viene creata una colonna di traduzione separata per ciascuna colonna per ciascuna lingua nella tabella. Lo svantaggio di questo approccio è che, ancora una volta, se viene aggiunto un gran numero di lingue, la struttura del database dovrà essere modificata ogni volta, e questo è considerato un approccio errato. Immaginate anche quanto saranno esagerati i segnali che chiedono l’internazionalizzazione. Potrebbe valere la pena considerare un modello in cui il numero di lingue supportate è noto in anticipo, non ce ne sono troppe e ogni modello dovrebbe esistere in tutte le varianti linguistiche. UML: Implementazione dell'applicazione multilingue - 12Tabella tutto compreso: Implementazione dell'applicazione multilingue - 13

4. Traduzione esterna

Questa opzione viene implementata collegando strumenti esterni (Google traduttore, Bing traduttore, ecc.). Viene utilizzato se è necessario fornire informazioni al maggior numero possibile di visitatori e ce ne sono molte. Così tanti. In questo caso puoi decidere di non memorizzare direttamente le informazioni nel database in tutte le lingue, ma di tradurle dinamicamente. Ma vale la pena ricordare che la qualità della traduzione automatica spesso lascia molto a desiderare. L'opzione può essere considerata solo molto economica (quando non ci sono risorse per tradurre ciascuna pubblicazione). Un problema comune in termini di traduzione corretta è che i traduttori che non conoscono bene la lingua scelgono il significato sbagliato di una parola e confondono l'utente, costringendolo a capire autonomamente il significato di ciò che è scritto sul pulsante. È anche importante non solo tradurre correttamente la frase, ma anche portare il suo significato a una lingua e nazionalità specifiche. Gli sviluppatori hanno molti problemi con i generi in alcune lingue. Devono duplicare la frase nel codice in base al sesso dell'utente e tenere conto del fatto che non solo i sostantivi hanno un genere, ma anche gli aggettivi e i verbi hanno una flessione diversa. Ci sono casi in cui, avendo selezionato nell'applicazione una lingua diversa dall'inglese, insieme alle parole della lingua selezionata, sono ancora presenti elementi non tradotti. È ancora peggio se vengono visualizzate più lingue e si scopre una sorta di Babilonia, dove tutto è confuso e l'utente non riesce a capire l'applicazione, ad esempio: https://cloud.google.com/translate/

5. File di supporto a livello di applicazione

Vengono creati file separati per archiviare le traduzioni. Può essere una lima per lingua oppure una lima per lingua per compressa (frantumazione fine). Questa opzione viene spesso utilizzata perché in questi file possono essere memorizzati molti testi, il che significa che le tabelle e il database stesso non si gonfieranno. La comodità sta anche nel fatto che non è necessario bussare al database per questi campi, e nel codice i file possono essere sostituiti dinamicamente a seconda della lingua richiesta. Di conseguenza, il file funge per noi da dizionario, in cui la chiave è la lingua, il valore è il testo. Ma non ci limitiamo al formato ".properties" riportato di seguito e questi formati di file possono variare ampiamente: JSON, XML, ecc. Gli svantaggi sono che in questo caso la normalizzazione del database è notevolmente ridotta. Inoltre, l’integrità dei dati non dipende più solo dal database, ma anche dal meccanismo di serializzazione. Eccellente articolo su questo argomento Esempio di file di dizionario con traduzioni: Implementazione dell'applicazione multilingue - 14
  • Inglese Implementazione dell'applicazione multilingue - 15
  • russo Implementazione dell'applicazione multilingue - 16
  • spagnolo Implementazione dell'applicazione multilingue - 17

6. Tabella di traduzione ausiliaria per ogni tabella

Secondo me la soluzione più flessibile. L'essenza di questo approccio è creare una tabella separata per le lingue. Quando è necessario implementare la possibilità di traduzioni per la tabella in questione, viene creato un collegamento con la tabella delle lingue, e la tabella di collegamento contiene l'id della lingua, l'id dell'elemento e le colonne con le traduzioni. Non è così spaventoso come sembra. Questo approccio consente un'estensibilità abbastanza flessibile delle lingue supportate. Diamo uno sguardo più da vicino. UML: Implementazione dell'applicazione multilingue - 18Tabella con filmati: Implementazione dell'applicazione multilingue - 19Tabella delle lingue: Implementazione dell'applicazione multilingue - 20Tabella delle traduzioni: Implementazione dell'applicazione multilingue - 21 E, come ho detto sopra, diamo un'occhiata all'implementazione di una delle opzioni nel codice Java (come hai capito, questa sarà l'ultima opzione). Non c'è niente di simile nell'applicazione stessa: passeremo dai controller ai livelli dao. Considereremo il metodo create: questo sarà sufficiente per fare un esempio. Quindi andiamo)) La nostra essenza è un film:
@Builder
@Getter
public class Movie {

   private Long id;

   private String producer;
}
Niente di interessante, solo implementando il modello della prima tabella. Controller con convertitori 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();
   }
}
In DTO passiamo le traduzioni come mappe, la chiave è l'abbreviazione della lingua, il valore è il valore della traduzione (nome del film). 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' : 'Матрица'}"
}
Qui vediamo la classe dto vera e propria, come scritto sopra, mappa per le traduzioni, i restanti campi sono una visualizzazione del modello Movie, passiamo al servizio movie:
public interface MovieService {

   Movie create(Movie movie, Map nameList);
}
La sua implementazione:
@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;
   }
}
Qui vediamo l'uso di un servizio LanguageService relativamente di terze parti per recuperare l'ID della lingua tramite la sua abbreviazione. E con questo identificatore salviamo le nostre traduzioni (anche sotto forma di mappa) nella tabella delle connessioni. Diamo un'occhiata al DAO:
public interface MovieDAO {

   void create(Movie movie);

   void createTranslator(Long movieId, Map<Long,String> nameTranslations);
}
Implementazione:
@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));
   }
}
Qui vediamo la conservazione dell'essenza e delle lingue per essa (dizionario). E sì, qui viene utilizzato Spring JDBC: lo ritengo preferibile per i principianti, poiché è più trasparente. Passiamo ad un servizio “di terze parti”. Servizio linguistico:
public interface LanguageService {

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

   @Override
   public Long getIdByLangCode(String lang) {
       return languageDAO.getIdByLangCode(lang);
   }
}
Niente di speciale, cerca per nome abbreviato. DAO:
public interface LanguageDAO {

   Long getIdByLangCode(String lang);
}
Implementazione:
@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);
   }
}
Struttura: Implementazione dell'applicazione multilingue - 23 Tutti i modelli sopra descritti hanno diritto alla vita. È necessario decidere quale utilizzare in base alla situazione. Naturalmente, non è tutto: esistono molti altri approcci diversi, incluso l’utilizzo di database diversi per lingue diverse, l’utilizzo di cache, framework diversi e così via. Per me oggi è tutto e... Implementazione dell'applicazione multilingue - 24
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION