Localização infinita ou como traduzimos um mapa em tempo real



O que acontece quando seu produto começa a ser vendido em outro país com seu próprio idioma e características culturais? Provavelmente, a localização o espera. Na maioria dos casos, é necessário apenas converter os arquivos de recursos para que os menus e os elementos da interface estejam no idioma familiar do usuário. Mas o que fazer se a base do que você vende são dados, que são muitos, eles vêm constantemente, em um grande volume e exigem tradução regular. E não apenas um idioma, mas vários idiomas ao mesmo tempo.

Abaixo do corte, você encontrará uma história de como esse problema foi resolvido no 2GIS. Vou lhe dizer o exemplo do último caso em Dubai, mas as práticas são aplicáveis ​​a qualquer idioma.

Sobre o árabe


Toda essa história começou com o lançamento do 2GIS em Dubai, onde dois idiomas estão em uso: árabe e inglês.

A alta precisão dos dados é o valor mais importante da empresa. Isso é alcançado através do trabalho manual de cartógrafos e especialistas no local. Em Dubai, onde especialistas locais e usuários finais conhecem inglês, inicialmente os dados foram inseridos apenas nele. No processo de crescimento, eles decidiram não parar por aí e adicionar o idioma árabe.

Fiji


Para o trabalho de cartógrafos, temos nosso próprio software. Essa coisa é chamada Fiji e serve como um sistema mestre para coletar dados do mapa. Já escrevemos como Fiji ajuda cartógrafos a editar casas e desenhar estradas. Os dados enviados de Fiji após o processamento e a preparação vão para o produto final para agradar aos usuários. No artigo, estou falando exatamente sobre o que implementamos nas Ilhas Fiji para editar / armazenar / exibir dados multilíngues.

Termos


Na equipe, usamos vocabulário específico. Abaixo estão quatro exemplos ↓
O sistema suporta o trabalho com dois tipos de idiomas:

Idioma dos metadados - o idioma no qual todos os controles do usuário são exibidos: interface do usuário, metadados.

Idioma dos dados - um idioma em que os valores dos atributos dos objetos geográficos, alguns diretórios e classificadores são exibidos.

Os idiomas estão vinculados aos territórios. Um território pode ter dois tipos de idiomas:

A língua principal do território é a língua adotada oficialmente nesse território.

O idioma adicional do território é o idioma em que queremos lançar o produto. Vem além do principal.

Idiomas e dialetos


Os dialetos adotados em diferentes regiões do país podem variar significativamente. Portanto, em alguns sistemas, o núcleo do idioma (= versão básica) e os dialetos são armazenados separadamente e, em seguida, ao descarregar seus merjats. Para nós, essa abordagem parecia muito complicada, por isso decidimos considerar cada dialeto como um idioma independente.

Nuance relacionada a idiomas e dialetos árabes. Para cada idioma, é necessário inserir um sinalizador de direção do carro com dois valores: da esquerda para a direita e da direita para a esquerda. Por padrão, o carro deve se mover da esquerda para a direita. Se o valor estiver definido da direita para a esquerda, será necessário alterar a direção do carro para todos os campos editáveis ​​e multilíngues. Como isso foi feito nos produtos finais foi escrito aqui . Nós tivemos que fazer o mesmo.

Ajustar aos territórios


Nosso mundo inteiro está dividido em certos territórios - podem ser países, regiões, regiões. Para cada território, especificamos várias línguas, uma das quais é considerada a principal e as demais - adicionais. A tradução ocorre do idioma principal para outros idiomas.

Por exemplo, no caso de Dubai, deixamos o inglês como idioma principal, porque havia muitos dados. O árabe foi tornado opcional.

Digite e mude o idioma


Para que os cartógrafos trabalhem confortavelmente, redesenhamos nossa interface naqueles lugares onde se entende a entrada multilíngue.



Nesta figura, você pode ver que dividimos os idiomas em abas, onde o mais à esquerda é o idioma principal e existem outros adicionais.

Nas guias de idiomas adicionais, apenas esses campos estão disponíveis para edição que possuem um sinalizador para a necessidade de tradução no banco de dados. Isso serve como uma medida protetora e ajuda a concentrar a atenção do usuário na tradução dos dados necessários. Todo o resto é editado no idioma principal.



De fato, a edição de dados em um idioma adicional pode ser necessária apenas se o próprio cartógrafo conhecer vários idiomas e não desejar recorrer à ajuda de um tradutor. Para todos os outros, existe o CrowdIn.

Crowdin, ou transferência de stream


Assim, permitimos que nossos cartógrafos preenchessem dados em diferentes idiomas. Mas é muito melhor entregar a tarefa de tradução a profissionais.

A primeira coisa que vem à mente ao traduzir o aplicativo é fornecer os arquivos de recursos aos tradutores e após a transferência, faça o download deles de volta.

Nesse caso, a plataforma CrowdIn nos ajudou muito. Ele permite que você redirecione seus arquivos para tradutores profissionais. A única coisa que restou foi integrar os dados traduzidos ao nosso sistema.

A situação é complicada pelo fato de os dados chegarem a nós em um fluxo contínuo; portanto, gostaríamos de receber traduções continuamente.

Otimizamos o sistema da seguinte forma: se forem feitas alterações no idioma principal do território, faremos o upload das alterações para tradução em todos os idiomas adicionais desse território. Abrimos exceções nos casos em que o próprio cartógrafo fez a tradução. Aqui acreditamos que ele entende o que está fazendo e não há necessidade de conectar um intérprete.

Para cada diretório ou objeto de mapa, temos uma versão ponta a ponta, que é incrementada a cada atualização de dados. Para que possamos obter rapidamente todas as alterações de uma versão específica.

O sistema de versão é muito simples e eficiente, mas tem uma desvantagem significativa: na verdade, temos uma fila única e não podemos gerenciá-lo de nenhuma maneira. Nosso máximo é pular a versão. É necessário mudar para uma fila normal, por exemplo, para RabitMQ ou Kafka, mas as mãos ainda não chegaram.

Para atualizar rapidamente o conteúdo, escrevemos um pequeno serviço que funciona em três fluxos.

O primeiro fluxo (Saver) extrai todos os dados que requerem tradução e gera arquivos xml a partir deles.

O segundo (Exportar) os envia ao CrowdIn e os coloca no projeto desejado, que indica o idioma principal do qual estamos traduzindo e uma lista de idiomas para os quais traduzir.

O terceiro (Importar) pesquisa periodicamente a API CrowdIn em busca de arquivos cuja tradução foi feita e instalada 100%, e importa os arquivos finalizados em nosso banco de dados.



Sem um ancinho não poderia fazer. Tropeçamos no nosso sistema de versão de dados.
Quando baixamos a tradução da palavra, a versão dos dados foi atualizada e a palavra novamente caiu na tradução.

Para evitar um ciclo interminável de tradução, começamos a registrar dados. Cada palavra traduzida é marcada, o que elimina o envio repetido para o CrowdIn.

Tutorial


Agora vou contar como acontece o trabalho com o CrowdIn. Existem várias maneiras de trabalhar com a plataforma, por exemplo, você pode fazer upload de arquivos para o repositório Git e o próprio CrowdIn os suga. Mas achamos que trabalhar com a API parece mais conveniente.

CrowdIn tem um tutorial bastante detalhado, mas abaixo vou escrever como fizemos.

Precisamos obter uma chave de API, que anexaremos a cada uma de nossas solicitações, para que o sistema nos verifique. Vamos para a guia API nas configurações do projeto e examinamos o que está escrito na coluna chave da API.



Essa chave deve ser adicionada no final de cada uma das suas solicitações de plataforma. Por exemplo, assim:
GET: https://api.crowdin.com/api/project/{myLitleProject}/download/all.zip?key={project-key}

2. Crie uma pasta na qual carregaremos os arquivos dentro do projeto.

 var uri = $"project/{_projectName}/add-directory?key={apiKey}"; var content = new MultipartFormDataContent { { new StringContent(crowdInDirectoryPath), "name" } }; return PostAsync(uri, content); 

Há um pequeno momento desajeitado. Estamos escrevendo um serviço, seria bom se, a princípio, ele verifique se a pasta de que precisamos está presente antes de tentar criá-lo. Como o CrowdIn não tem uma maneira normal de verificar uma pasta, enviamos uma solicitação de criação. Se não estiver lá, o CrowdIn o criará e retornará o código 200. Se houver uma pasta, ele não criará nada e retornará o código 500.

3. Exporte arquivos. A função add-file possui muitas opções e parâmetros, como ler e onde. Abaixo está um exemplo de como carregamos dados com arquivos xml.

Exemplo
Colocamos todos os dados que vamos traduzir em arquivos xml com a seguinte estrutura.

 <LocalizableDocument> <LocalizableValues> <LocalizableValue><Attributes> <LocalizableAttributeValue> <AttributeName/> <Value/> </LocalizableAttributeValue> </Attributes> </LocalizableValue> </LocalizableValues> </LocalizableDocument> 

Para que o CrowdIn analise quais dados do arquivo precisam ser traduzidos, eles precisam ser especificados. Para fazer isso, você precisa escrever uma matriz de parâmetros translatable_elements com os caminhos para os elementos necessários do documento no conteúdo. No nosso caso, ficou assim:

 var uri = $"project/{_projectName}/add-file?key={apiKey}"; var content = new MultipartFormDataContent { { new StringContent("/LocalizableDocument/LocalizableValues/LocalizableValue/Attributes/LocalizableAttributeValue/Value"), "translatable_elements[0]" } }; foreach (var filePath in filePaths) { var fileName = Path.GetFileName(filePath); var fileStream = File.OpenRead(filePath); var fileContent = new StreamContent(fileStream); content.Add(fileContent, $"files[{_crowdInDirectoryPath}/{fileName}]", fileName); } return PostAsync(uri, content); 


Observe: a documentação afirma que o CrowdIn pode mastigar no máximo 20 arquivos por vez, enquanto o tamanho de um arquivo não deve exceder 100 MB.

4. Descobrimos quais arquivos foram totalmente traduzidos. Fazemos isso usando um comando para um idioma específico.

 var uri = $"project/{_projectName}/language-status?key={apiKey}"; var content = new MultipartFormDataContent {{ new StringContent(langCode), "language" } }; return PostAsync(uri, content); 

A plataforma nos retornará algo como isto:

  <item> <node_type>directory</node_type> <id>29812</id> <name>Version 1.0</name> <files> <item> <node_type>file</node_type> <id>29827</id> <name>strings.xml</name> <node_type>file</node_type> <phrases>7</phrases> <translated>0</translated> <approved>0</approved> <words>32</words> <words_translated>0</words_translated> <words_approved>0</words_approved> </item> </files> </item> 

Aqui estamos interessados ​​nos valores <traduzidos /> e <aprovados />. O primeiro indica a porcentagem de linhas traduzidas nesse arquivo, o segundo mostra a porcentagem de valores aprovados se, além do tradutor, um revisor também participar do fluxo de trabalho. Dependendo do nosso fluxo de trabalho, por exemplo, em 100, consideramos o documento traduzido e aprovado. Agora, esse arquivo pode ser importado de volta para nós.

5. Importe o arquivo de volta para o nosso sistema.
Isso é feito com uma simples solicitação GET.

 https://api.crowdin.com/api/project/{_projectName}/export-file?file={_crowdInDirectoryPath}/{fileName}&language={langCode}&key={project-key} 

O arquivo resultante é desserializado e os dados são importados para o nosso sistema.

Em vez de uma conclusão


Em termos gerais, é tudo. Obviamente, ainda era necessário refinar a exibição das assinaturas no mapa de Fiji para que elas fossem exibidas no idioma correto, dependendo do território em que o cartógrafo agora governa. Era necessário concordar com outros sistemas como fornecê-los com dados multilíngues, mas isso é outra história.

Como resultado, recebemos o serviço com o espírito de "ligado e esquecido". Os cartógrafos inserem dados, os tradutores traduzem, o sheik está satisfeito, o serviço faz o upload dos dados quando necessário e resolvemos problemas mais urgentes sem pensar em como o nosso sistema funciona em vários idiomas.

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


All Articles