Compatibilidade com diferentes idiomas e culturas

Os apps incluem recursos que podem ser específicos para uma determinada cultura. Por exemplo, um app pode incluir strings próprias de uma cultura que são traduzidas para o idioma da localidade atual. É recomendável manter os recursos específicos da cultura separados do restante do app. O Android resolve recursos específicos de idioma e cultura com base na configuração de localidade do sistema. Você pode oferecer compatibilidade com diferentes localidades usando o diretório de recursos no seu projeto Android.

Você pode especificar recursos adaptados à cultura das pessoas que usam seu app e fornecer qualquer tipo de recurso apropriado para o idioma e a cultura dos seus usuários. Por exemplo, a captura de tela a seguir mostra um app exibindo recursos desenháveis e de string na localidade padrão do dispositivo (en_US) e em espanhol (es_ES).

O app mostra um
texto e um ícone diferentes, dependendo da localidade atual

Figura 1. Um app que usa recursos diferentes dependendo da localidade atual.

Caso você tenha criado o projeto usando as Ferramentas do SDK do Android (consulte Criar um projeto para Android), as ferramentas criarão um diretório res/ no nível superior do projeto. Dentro desse diretório res/, há subdiretórios para diversos tipos de recursos. Há também alguns arquivos padrão, como res/values/strings.xml, que armazenam os valores da string.

A compatibilidade com diferentes idiomas vai além do uso de recursos específicos de localidade. Alguns usuários escolhem um idioma que usa scripts da direita para a esquerda (RTL), como árabe ou hebraico, para a localidade da IU. Outros usuários visualizam ou geram conteúdo em um idioma que usa scripts RTL, mesmo que tenham definido um idioma que usa scripts da esquerda para a direita (LTR), como o inglês, como a localidade da IU. Para ser compatível com os dois tipos de usuários, seu app precisa fazer o seguinte:

  • Empregar um layout de IU RTL para localidades RTL.
  • Detectar e declarar a direção dos dados de texto que são exibidos em mensagens formatadas. Normalmente, basta chamar um método que determine a direção dos dados de texto para você.

Criar diretórios de localidade e arquivos de recursos

Para aumentar a compatibilidade com mais localidades, crie mais diretórios dentro de res/. O nome de cada diretório precisa seguir este formato:

<resource type>-b+<language code>[+<country code>]

Por exemplo, values-b+es/ contém recursos de string para localidades com o código de idioma es. Da mesma forma, mipmap-b+es+ES/ contém ícones para localidades com o código do idioma es e o código do país ES. O Android carrega os recursos apropriados de acordo com as configurações de localidade do dispositivo no momento da execução. Para mais informações, consulte Como fornecer recursos alternativos.

Depois de decidir para quais localidades seu app oferecerá compatibilidade, crie os subdiretórios e arquivos de recursos. Exemplo:

MyProject/
    res/
       values/
           strings.xml
       values-b+es/
           strings.xml
       mipmap/
           country_flag.png
       mipmap-b+es+ES/
           country_flag.png

Por exemplo, a seguir estão diferentes arquivos de recursos para diversos idiomas:

Strings em inglês (localidade padrão), /values/strings.xml:

<resources>
    <string name="hello_world">Hello World!</string>
</resources>

Strings em espanhol (localidade es), /values-es/strings.xml:

<resources>
    <string name="hello_world">¡Hola Mundo!</string>
</resources>

Ícone da bandeira dos Estados Unidos (localidade padrão), /mipmap/country_flag.png:

O ícone da bandeira dos
Estados Unidos

Figura 2. Ícone usado para a localidade padrão (en_US).

Ícone da bandeira da Espanha (localidade es_ES), /mipmap-b+es+ES/country_flag.png:

O ícone da bandeira da
Espanha

Figura 3. Ícone usado para a localidade es_ES.

Observação: é possível usar o qualificador de localidade (ou qualquer qualificador de configuração) em qualquer tipo de recurso. Por exemplo, você pode fornecer versões localizadas do seu drawable de bitmap. Para mais informações, consulte Localização.

Usar os recursos no seu app

Você pode referenciar os recursos no seu código-fonte e em outros arquivos XML usando o atributo name de cada recurso.

No código-fonte, é possível referenciar um recurso de string com a sintaxe R.<resource type>.<resource name>. Há diversos métodos que aceitam um recurso dessa maneira.

Exemplo:

Kotlin

// Get a string resource from your app's Resources
val hello = resources.getString(R.string.hello_world)

// Or supply a string resource to a method that requires a string
TextView(this).apply {
    setText(R.string.hello_world)
}

Java

// Get a string resource from your app's Resources
String hello = getResources().getString(R.string.hello_world);

// Or supply a string resource to a method that requires a string
TextView textView = new TextView(this);
textView.setText(R.string.hello_world);

Em outros arquivos XML, você poderá referenciar um recurso de string com a sintaxe @<resource type>/<resource name> sempre que o atributo XML aceitar um valor compatível.

Exemplo:

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@mipmap/country_flag" />

Formatar texto em mensagens

Uma das tarefas mais comuns em um app é a formatação de texto. As mensagens localizadas são formatadas ao inserir texto e dados numéricos nas posições apropriadas. Ao lidar com uma IU ou com dados RTL, a formatação simples pode exibir textos incorretos ou mesmo ilegíveis.

Em geral, idiomas como árabe, hebraico, persa e urdu são escritos na direção RTL. No entanto, alguns dos elementos, como números e texto LTR incorporados, são escritos na direção LTR dentro do texto RTL. Os idiomas que usam scripts LTR, incluindo o inglês, também são bidirecionais, porque podem conter scripts RTL incorporados que precisariam ser exibidos na direção RTL.

Na maioria das vezes, são os próprios apps que geram essas instâncias de texto de direção oposta incorporadas. Eles inserem dados de texto de um idioma arbitrário — e uma direção de texto arbitrária — em mensagens localizadas. Essa mistura de direções não costuma incluir uma indicação clara de onde o texto em direção oposta começa e termina. Essas características do texto gerado pelo app causam a maior parte dos problemas.

Embora o tratamento padrão do sistema de texto bidirecional normalmente renderize o texto conforme o esperado, é possível que ele não seja renderizado corretamente quando o app o inserir em uma mensagem localizada. As seguintes situações apresentam exemplos de casos em que há uma grande probabilidade de que o texto não apareça corretamente:

  • Inserido no começo da mensagem:

    PERSON_NAME está ligando para você

  • Começa com um número, como em endereços ou números de telefone:

    987 654-3210

  • Começa com pontuação, como em um número de telefone:

    +19876543210

  • Termina com pontuação:

    Tem certeza?

  • Já tem as duas direções:

    A palavra בננה significa banana em hebraico.

Exemplo

Por exemplo, suponha que um app às vezes precise exibir a mensagem “Você quis dizer %s?”, com um endereço inserido no lugar de “%s” no momento da execução. Como o app é compatível com IUs para localidades diferentes, a mensagem é proveniente de um recurso específico de localidade e usa a direção RTL quando uma localidade RTL está em uso. Para uma IU em hebraico, a mensagem seria exibida assim:

האם התכוונת ל %s?

No entanto, a sugestão pode vir de um banco de dados que não inclui texto no idioma da localidade. Por exemplo, se o endereço em questão for de um lugar na Califórnia, ele aparecerá no banco de dados com texto em inglês. Se você inserir o endereço "15 Bay Street, Laurel, CA" na mensagem RTL sem fornecer quaisquer dicas sobre a direção do texto, o resultado não será o esperado nem correto:

האם התכוונת ל 15 Bay Street, Laurel, CA?

Veja que o número da casa aparece à direita do endereço, e não à esquerda, como seria esperado. Isso faz com que o número pareça um código postal estranho. O mesmo problema pode ocorrer se você incluir texto RTL em uma mensagem que usa a direção de texto LTR.

Explicação e solução

O problema do exemplo anterior ocorre porque o formatador de texto não especifica que "15" faz parte do endereço. Por isso, o sistema não pode determinar se "15" faz parte do texto RTL que vem antes dele ou do texto LTR que vem depois.

Para resolver esse problema, use o método unicodeWrap(), encontrado na classe BidiFormatter, em todas as partes do texto que você inserir em mensagens localizadas. Estas são as únicas situações em que não é recomendável usar unicodeWrap():

  • O texto está inserido em uma string legível por máquina, como uma consulta de URI ou SQL.
  • Você já sabe que a formatação do texto está correta.

O método unicodeWrap() detecta a direção de uma string e une-a em caracteres de formatação Unicode que declaram essa direção. Como o "15" agora está dentro do texto declarado como LTR, ele será exibido na posição correta:

האם התכוונת ל 15 Bay Street, Laurel, CA?

O snippet de código a seguir demonstra como usar unicodeWrap():

Kotlin

val mySuggestion = "15 Bay Street, Laurel, CA"
val myBidiFormatter: BidiFormatter = BidiFormatter.getInstance()

// The "did_you_mean" localized string resource includes
// a "%s" placeholder for the suggestion.
String.format(getString(R.string.did_you_mean), myBidiFormatter.unicodeWrap(mySuggestion))

Java

String mySuggestion = "15 Bay Street, Laurel, CA";
BidiFormatter myBidiFormatter = BidiFormatter.getInstance();

// The "did_you_mean" localized string resource includes
// a "%s" placeholder for the suggestion.
String.format(getString(R.string.did_you_mean),
        myBidiFormatter.unicodeWrap(mySuggestion));

Observação: caso seu app seja destinado ao Android 4.3 (nível 18 da API) ou mais recente, use a versão do BidiFormatter disponível no framework do Android. Caso contrário, use a versão do BidiFormatter disponível na Biblioteca de Suporte.

Formatar números

Use strings de formato, e não chamadas de método, para converter números em strings na lógica do app:

Kotlin

var myIntAsString = "$myInt"

Java

String myIntAsString = String.format("%d", myInt);

Isso formatará os números corretamente para sua localidade, o que pode incluir o uso de um conjunto diferente de dígitos.

Ao usar String.format() para criar uma consulta SQL em um dispositivo com uma localidade que usa o próprio conjunto de dígitos, como persa e a maioria das localidades em árabe, poderá haver problemas se os parâmetros da consulta forem números. Isso acontece porque o número é formatado nos dígitos da localidade, que são inválidos em SQL.

Para preservar números formatados em ASCII e manter a consulta SQL válida, use a versão sobrecarregada de String.format(), que inclui uma localidade como primeiro parâmetro. O argumento da localidade precisa ser Locale.US.

Compatibilidade com o espelhamento de layout

As pessoas que usam scripts RTL preferem uma interface de usuário RTL, o que inclui menus e textos alinhados à direita, além de setas de encaminhamento apontando para a esquerda.

A Figura 4 mostra o contraste entre a versão LTR de uma tela do app Configurações e a versão RTL equivalente:

A área de notificação está alinhada à direita, próxima ao canto superior direito,
           o botão de menu na barra de apps está próximo ao canto superior esquerdo, o
           conteúdo da parte principal da tela está alinhado à esquerda
           em ordem LTR, e o botão &quot;Voltar&quot; está próximo ao canto inferior esquerdo da tela,
           apontando para a esquerda. A área de notificação está alinhada à esquerda, próxima ao canto superior esquerdo, o
            botão de menu na barra de apps está próximo ao canto superior direito, o
            conteúdo da parte principal da tela está alinhado à direita em ordem RTL, e
            o botão &quot;Voltar&quot; está próximo ao canto inferior direito da tela, apontando para
            a direita.
Figura 4. Variantes LTR e RTL de uma tela.

Ao adicionar compatibilidade com RTL ao seu app, é particularmente importante lembrar do seguinte:

  • O espelhamento de texto RTL só é compatível com apps quando usado em dispositivos com o Android 4.2 (nível 17 da API) ou mais recente. Para saber como oferecer compatibilidade com o espelhamento de texto em dispositivos mais antigos, consulte Oferecer compatibilidade com apps legados.
  • Para testar se o app é compatível com a direção de texto RTL, use as opções do desenvolvedor para testá-lo e convide pessoas que usam scripts RTL para usar o app.

Observação: para ver outras diretrizes de design relacionadas ao espelhamento de layout, incluindo uma lista de elementos que você precisa ou não espelhar, consulte as diretrizes do Material Design para Bidirecionalidade (link em inglês).

Ao espelhar o layout da IU no seu app para que ele seja exibido como RTL em uma localidade RTL, conclua as etapas das seções a seguir.

Modificar os arquivos de versão e manifesto

Modifique o arquivo build.gradle do módulo do app e o arquivo de manifesto da seguinte maneira:

build.gradle (Module: app)

Groovy

android {
    ...
    defaultConfig {
        targetSdkVersion 17 // Or higher
        ...
    }
}

Kotlin

android {
    ...
    defaultConfig {
        targetSdkVersion(17) // Or higher
        ...
    }
}

AndroidManifest.xml

<manifest ... >
    ...
    <application ...
        android:supportsRtl="true">
    </application>
</manifest>

Observação: caso seu app seja destinado ao Android 4.1.1 (nível 16 da API) ou anterior, o atributo android:supportsRtl será ignorado, junto dos valores start e end que aparecem nos arquivos de layout do app. Nesse caso, o espelhamento de layout RTL não acontecerá automaticamente no app.

Atualizar os recursos existentes

Converta left e right para start e end, respectivamente, em cada um dos arquivos de recursos de layout existentes. Ao fazer isso, você permite que o framework alinhe os elementos da IU do app com base nas configurações de idioma do usuário.

Observação: antes de atualizar seus recursos, aprenda a oferecer compatibilidade com apps legados ou apps destinados ao Android 4.1.1 (nível 16 da API) e versões anteriores.

Para usar os recursos de alinhamento RTL do framework, altere os atributos nos seus arquivos de layout que aparecem na Tabela 1.

Tabela 1. Atributos que serão usados quando seu app for compatível com várias direções de texto.

Atributo compatível somente com LTR Atributo compatível com LTR e RTL
android:gravity="left" android:gravity="start"
android:gravity="right" android:gravity="end"
android:layout_gravity="left" android:layout_gravity="start"
android:layout_gravity="right" android:layout_gravity="end"
android:paddingLeft android:paddingStart
android:paddingRight android:paddingEnd
android:drawableLeft android:drawableStart
android:drawableRight android:drawableEnd
android:layout_alignLeft android:layout_alignStart
android:layout_alignRight android:layout_alignEnd
android:layout_marginLeft android:layout_marginStart
android:layout_marginRight android:layout_marginEnd
android:layout_alignParentLeft android:layout_alignParentStart
android:layout_alignParentRight android:layout_alignParentEnd
android:layout_toLeftOf android:layout_toStartOf
android:layout_toRightOf android:layout_toEndOf

A Tabela 2 mostra como o sistema processa os atributos de alinhamento da IU com base na versão do SDK de destino, estejam os atributos left e right, bem como os atributos start e end, definidos ou não.

Tabela 2. Comportamento de alinhamento do elemento da IU com base na versão do SDK de destino e nos atributos definidos.

Destinado ao Android 4.2
(nível 17 da API) ou mais recente?
Esquerda e direita definidas? Início e término definidos? Resultado
Sim Sim Sim start e end resolvidos e sobreposição de left e right
Sim Sim Não Somente left e right são usados
Sim Não Sim Somente start e end são usados
Não Sim Sim left e right são usados (start e end são ignorados)
Não Sim Não Somente left e right são usados
Não Não Sim start e end resolvidos para left e right

Adicionar recursos específicos de direção e idioma

Nesta etapa, serão adicionadas versões específicas dos seus arquivos de recursos de valor, layout e drawables que contém valores personalizados para diferentes idiomas e direções de texto.

No Android 4.2 (nível 17 da API) ou mais recente, você pode usar os qualificadores de recurso -ldrtl (layout da direita para a esquerda) e -ldltr (layout da esquerda para a direita). Para manter a compatibilidade com versões anteriores do carregamento de recursos existentes, as versões mais antigas do Android usam os qualificadores de idioma de um recurso para inferir a direção correta do texto.

Suponha que você queira adicionar um arquivo de layout específico para ser compatível com scripts RTL, como os idiomas hebraico, árabe e persa. Para fazer isso, adicione um diretório layout-ldrtl/ ao diretório res/, conforme mostrado no seguinte exemplo:

res/
    layout/
        main.xml This layout file is loaded by default.
    layout-ldrtl/
        main.xml This layout file is loaded for languages using an
                 RTL text direction, including Arabic, Persian, and Hebrew.

Se você quiser adicionar uma versão específica do layout criada apenas para texto em árabe, a estrutura do diretório será a seguinte:

res/
    layout/
        main.xml This layout file is loaded by default.
    layout-ar/
        main.xml This layout file is loaded for Arabic text.
    layout-ldrtl/
        main.xml This layout file is loaded only for non-Arabic
                 languages that use an RTL text direction.

Observação: os recursos específicos de idioma têm precedência sobre os recursos específicos de direção do layout, que têm precedência sobre os recursos padrão.

Usar widgets compatíveis

A partir do Android 4.2 (nível 17 da API), a maior parte dos elementos da IU do framework é automaticamente compatível com a direção de texto RTL. No entanto, vários elementos do framework, como ViewPager, não são compatíveis com a direção de texto RTL.

Os widgets da tela inicial são compatíveis com a direção do texto RTL, desde que os arquivos de manifesto correspondentes incluam a atribuição do atributo android:supportsRtl="true".

Oferecer compatibilidade com apps legados

Caso seu app seja direcionado ao Android 4.1.1 (nível 16 da API) ou versões mais recentes, inclua também os atributos left e right, além de start e end.

Para verificar se o layout precisa usar a direção de texto RTL, use a seguinte lógica:

Kotlin

private fun shouldUseLayoutRtl(): Boolean {
    return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
        View.LAYOUT_DIRECTION_RTL == layoutDirection
    } else {
        false
    }
}

Java

private boolean shouldUseLayoutRtl() {
    if (android.os.Build.VERSION.SDK_INT >=
            android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
        return View.LAYOUT_DIRECTION_RTL == getLayoutDirection();
    } else {
        return false;
    }
}

Observação: para evitar problemas de compatibilidade, use a versão 23.0.1 ou mais recente das Ferramentas de build do SDK do Android.

Testar o uso de opções do desenvolvedor

Em dispositivos com o Android 4.4 (nível 19 da API) ou versões mais recentes, é possível ativar a configuração Forçar layout da direita p/ esquerda nas Opções do desenvolvedor no dispositivo. Com essa configuração, você pode ver o texto que usa scripts LTR, como texto em inglês, no modo RTL.

Atualizar a lógica do app

Esta seção descreve locais específicos que precisam ser atualizados na lógica do seu app ao adaptá-lo para processar várias direções de texto.

Alterações de propriedade

Para gerenciar uma mudança em qualquer propriedade relacionada a RTL, como direção e parâmetros de layout, preenchimento, direção e alinhamento de texto ou posicionamento de drawables, use o callback onRtlPropertiesChanged(). Esse callback permite ver a direção atual do layout e atualizar os objetos View de uma atividade corretamente.

Visualizações

Se você estiver criando um widget de IU que não faça parte diretamente da hierarquia de visualização de uma atividade, como uma caixa de diálogo ou um elemento semelhante a um aviso, defina a direção correta do layout com base no contexto. O snippet de código a seguir demonstra como concluir esse processo:

Kotlin

val config: Configuration = context.resources.configuration
view.layoutDirection = config.layoutDirection

Java

final Configuration config =
    getContext().getResources().getConfiguration();
view.setLayoutDirection(config.getLayoutDirection());

Vários métodos da classe View precisam de considerações adicionais:

onMeasure()
As medidas de visualização podem variar, dependendo da direção do texto.
onLayout()
Se criar o próprio layout de implementação, você precisará chamar super() na sua versão de onLayout() e adaptar a lógica personalizada para que ela seja compatível com scripts RTL.
onDraw()
Se você estiver implementando uma visualização personalizada ou adicionando uma funcionalidade avançada a um desenho, atualize seu código para oferecer compatibilidade com scripts RTL. Use o seguinte código para determinar se o widget está no modo RTL:

Kotlin

// On devices running Android 4.1.1 (API level 16) and lower,
// you can call the isLayoutRtl() system method directly.
fun isLayoutRtl(): Boolean = layoutDirection == LAYOUT_DIRECTION_RTL

Java

// On devices running Android 4.1.1 (API level 16) and lower,
// you can call the isLayoutRtl() system method directly.
public boolean isLayoutRtl() {
    return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}

Drawables

Se você tiver um drawable que precise ser espelhado para um layout de RTL, conclua uma destas etapas com base na versão de Android do dispositivo:

  • Em dispositivos com o Android 4.3 (nível 18 da API) e versões anteriores, você precisa adicionar e definir arquivos de recurso -ldrtl.
  • No Android 4.4 (nível 19 da API) e versões mais recentes, você pode usar android:autoMirrored="true" ao definir seu drawable, o que permite ao sistema processar o espelhamento de layout RTL.

    Observação: o atributo android:autoMirrored funciona apenas para drawables simples, em que o espelhamento bidirecional é simplesmente um espelhamento gráfico do drawable como um todo. Caso o drawable contenha vários elementos ou se refleti-lo mudaria a interpretação dele, faça o espelhamento manualmente. Sempre que possível, verifique com um especialista em bidirecionamento se os drawables espelhados fazem sentido para os usuários.

Gravidade

Se o código do seu app estiver usando Gravity.LEFT ou Gravity.RIGHT, você precisará alterar esses valores para Gravity.START e Gravity.END, respectivamente.

Por exemplo, se você estiver usando o seguinte código:

Kotlin

when (gravity and Gravity.HORIZONTAL_GRAVITY_MASK) {
    Gravity.LEFT -> {
        // Handle objects that are left-aligned.
    }
    Gravity.RIGHT -> {
        // Handle objects that are right-aligned.
    }
}

Java

switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
    case Gravity.LEFT:
        // Handle objects that are left-aligned.
        break;
    case Gravity.RIGHT:
        // Handle objects that are right-aligned.
        break;
}

… você precisará alterá-lo para:

Kotlin

val absoluteGravity: Int = Gravity.getAbsoluteGravity(gravity, layoutDirection)
when (absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK) {
    Gravity.LEFT -> {
        // Handle objects that are left-aligned.
    }
    Gravity.RIGHT -> {
        // Handle objects that are right-aligned.
    }
}

Java

final int layoutDirection = getLayoutDirection();
final int absoluteGravity =
        Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
    case Gravity.LEFT:
        // Handle objects that are left-aligned.
        break;
    case Gravity.RIGHT:
        // Handle objects that are right-aligned.
        break;
}

Isso significa que você pode manter seu código existente que processa valores alinhados à esquerda e à direita, mesmo que esteja usando start e end para seus valores de gravidade.

Observação: ao aplicar as configurações de gravidade, use uma versão sobrecarregada de Gravity.apply() que inclua um argumento layoutDirection.

Margem e padding

Para oferecer compatibilidade com scripts RTL no app, siga estas práticas recomendadas relacionadas aos valores de margem e de preenchimento:

Veja também

Outros recursos

Para saber mais sobre como oferecer compatibilidade com dispositivos mais antigos, acesse os seguintes recursos:

Postagens do blog (em inglês)