quarta-feira, 22 de abril de 2015

Traduzindo aplicativos Android

Muita gente me pergunta como se traduz aplicativos Android, fazendo com que os textos apareçam na língua configurada pelo usuário no dispositivo.


Exemplo de aplicativo traduzido (tradeTeller) que se adapta
automaticamente à língua configurada no dispositivo.

Neste post aprenderemos exatamente como fazê-lo. Veremos que é algo bastante simples (embora um pouco trabalhoso), pois o framework já previa um ambiente internacionalizado desde sua concepção.

A abordagem é análoga à adotada na questão da parametrização para resolver a aparência do aplicativo em tamanhos de tela diferentes, quando fizemos (por ocasião do post: Como garantir a mesma aparência em qualquer tamanho de tela) uso dos arquivos "dimens.xml" para definir os tamanhos de fonte proporcionais a cada dimensão.

Na tradução de aplicativos, devemos pensar em duas situações. A primeira diz respeito aos textos fixos que aparecerão nas telas do aplicativo. Para que estejam na língua correta,  é preciso criar várias versões do arquivo "strings.xml", uma para cada língua em que desejamos disponibilizar nosso app. Esse arquivo está localizado na pasta "res/layout/values" e sua função é armazenar todos os textos que aparecem em nosso aplicativo. A segunda situação é que o aplicativo certamente precisará ter mensagens de alerta ou de erro, que precisarão de alguma intervenção, apresentando uma afirmação ou questionamento e pedindo uma ação do usuário. Para que, também, esses alertas apareçam na língua correta, devemos recuperar em tempo de execução os textos parametrizados.

Strings.xml

O framework do Android pressupõe que todas as frases que aparecem em uma tela de um app estejam parametrizadas, isto é, no design da tela (arquivos xml do diretório "res/layout") jamais deve haver um texto "hard-coded" (a frase propriamente dita, escrita por extenso dentro de um "android:text="). Vejamos um exemplo, para entender melhor:

Errado (hard-coded):

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Olá mundo!" />


Certo:

activity_mail.xml:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/textoOlaMundo" />

strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">meuPrimeiroApp</string>
    <string name="action_settings">Configurações</string>
    <string name="textoOlaMundo">Olá mundo!</string>
</resources>


Os textos que especificamos nos desenhos de tela (arquivos xml da res/layout) devem ser, todos, parametrizados (conforme o exemplo) apontando sempre para um valor no arquivo "res/values/strings.xml".

A razão desta pequena regra, que à princípio mais nos parece um mero aborrecimento, é justamente para que possamos traduzir o aplicativo.

Vamos ver então, como faríamos para disponibilizar um app em inglês, por exemplo. Para isto, precisaremos criar uma pasta embaixo de "res", conforme ilustrado na figura:




Vamos abrir o projeto "meuPrimeiroApp"  e clicar com o botão direito do mouse sobre a pasta "res". Depois selecionemos a opção "New->Folder". Na tela seguinte, digite, no campo folder name: "values-en".

Esta pasta é o local onde o framework do Android espera que seja colocado o arquivo strings.xml com os textos traduzidos para a língua inglesa (analogamente, se quiséssemos traduzir para o francês, criaríamos a pasta values-fr ou values-es para o espanhol).

Agora o segundo passo: Abrirmos a pasta "values" e localizarmos o arquivo "strings.xml". Selecionando-o através de um clique com o botão esquerdo do mouse. Digitando CTRL+C copiamos o arquivo. Depois selecionemos a pasta "values-en" recém criada. Digitando CTRL+V colamos ali o arquivo.

Temos agora uma cópia idêntica do strings.xml na pasta values-en. Vamos modificá-lo para que fique assim:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">myFirstApp</string>
    <string name="action_settings">Settings</string>
    <string name="textoOlaMundo">Hello world!</string>
</resources>

Ou seja, com os parâmetros traduzidos para o inglês.

Verifique o arquivo original. Ele deve estar com os parâmetros em português, assim:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">meuPrimeiroApp</string>
    <string name="action_settings">Configurações</string>
    <string name="textoOlaMundo">Olá mundo!</string>
</resources>

Cuidado para não confundir os dois. É um erro muito comum, visto que as duas abas ficam lado a lado no topo da tela de edição do código fonte.

Note também, que o strings.xml da pasta "values" principal, é o arquivo default. Portanto, caso o dispositivo Android não encontre o arquivo correspondente à língua configurada no aparelho, assumirá o "res/values/strings.xml" como o padrão.

O arquivo "res/layout/activity_main.xml" deve ficar assim:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/textoOlaMundo" />

</RelativeLayout>

Grave tudo com CTRL+S. Depois compile o projeto e veja o que aparece no seu celular:





Agora começa a brincadeira. Sem fazer nenhuma alteração no código-fonte e nem compilar novamente, configure o seu celular para funcionar na língua inglesa. Desta forma:




Após a alteração, perceba que o nome do aplicativo já aparece em inglês. Abrindo a aplicação, já será possível ver as mudanças:




Entretanto, apesar da tradução dos textos fixos ter funcionado corretamente, podemos ver que as mensagens de alerta que mostramos nos eventos (onCreate(), onStart(), etc.) ainda estão em português!

Isso acontece porque elas são geradas em tempo de execução. E as instruções que as imprime estão na classe que corresponde à tela (MainActivity.java).

É esta a segunda situação que mencionei no início do post.

Para resolver, não é complicado. Primeiro, vamos abrir os dois arquivos strings.xml: o da pasta values padrão e o da pasta values-en (português e inglês) e acrescentar, respectivamente,  as linhas abaixo entre os tags <resources>...</resources>:

res/values/strings.xml:

<string name="mensagemOnCreate">Passei no onCreate()</string>

res/values-en/strings.xml:

<string name="mensagemOnCreate">onCreate() was called!</string>


Agora, vamos abrir a classe "MainActivity.java" da pasta "src". Vamos alterar apenas o método onCreate(), para exemplificar o funcionamento:

   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      // Envia SMS
      //SmsManager smsManager=SmsManager.getDefault();
      //smsManager.sendTextMessage("99999-9999", null, "Este texto foi enviado por SMS", null, null);
      
      String textoOnCreate = getResources().getString(R.string.mensagemOnCreate);
      Toast.makeText(getApplicationContext(), textoOnCreate, Toast.LENGTH_SHORT).show();     
   }

A linha destacada em azul, obtém a frase correta, a partir dos recursos (resources), mas em tempo de execução. Assim, a mensagem de alerta também aparecerá traduzida, pois está, agora, parametrizada.

Será necessário recompilar o código para verificar as alterações. Grave tudo com CTRL+S, compile e veja que, a partir de agora, a mensagem inicial, chamada no onCreate() já aparece em inglês!

Para quem preferir baixar o código-fonte pronto, ao invés de digitar, disponibilizo-o aqui para download. Instruções para carregar o projeto no workspace do Eclipse aqui.

É claro, a tradução do app para outra língua pressupõe que nós a conheçamos. Mas a Google também pensou no caso em que desejemos traduzi-lo para uma língua que não falamos. Aí entra a brilhante solução do trabalho colaborativo.

No painel do desenvolvedor da Google, há uma opção para requisitar a tradução do arquivo strings.xml por parceiros, através de trabalho colaborativo (mas pago). Assim, se quisermos traduzir o aplicativo para, digamos, russo, isto é possível, através desta ferramenta:





Depois é preciso enviar o arquivo xml e escolher para qual língua iremos traduzir e também qual o parceiro que fará o trabalho, de acordo com o prazo (cerca de 10 dias) e o preço (cerca de 30 dólares):





Ufa! Este foi um post extenso. Mas acredito ter sido bem esclarecedor para quem sempre se perguntou como um app pode se adaptar ao idioma configurado em um aparelho Android. Como vimos, é mais simples do que se imagina, embora um pouco trabalhoso.

Com a prática no desenvolvimento de aplicativos, isso se torna rotineiro. Normalmente deixa-se a tradução para o final, quando os arquivos de parâmetros já estão fechados. O mais importante é tomar o cuidado para só usar, tanto nos layouts de tela, quanto nas classes, sempre referências aos parâmetros e nunca os textos escritos por extenso.

Ficamos por aqui. Até a próxima!

------------------------------------------------------



Atualização do post:

Informação adicional, a respeito dos códigos acrescentados ao nome das pastas "values", para guardar o arquivo "strings.xml" relativo a cada idioma: as duas letras acrescentadas após o hífen são um código de idioma definido no padrão ISO 639-1. Opcionalmente, pode-se acrescentar ainda mais um hífen e a letra "r" (minúscula) representando uma região, seguida pelo seu código, no padrão ISO 3166-1-alpha-2.

Para português brasileiro, por exemplo, o nome da pasta ficaria "values-pt-rBR".

2 comentários:

  1. Em casos como tradução de arquivos em formato XML, você pode utilizar uma plataforma de crowdsourcing baseada na web, como https://poeditor.com

    É uma excelente ferramenta colaborativa para criando apps Android multi-idioma.

    ResponderExcluir