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. É uma prática recomendada manter os recursos culturais particulares separados do restante do app. O Android resolve recursos culturais e de idiomas específicos 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.

Também é possível utilizar recursos adaptados à cultura das pessoas que usam seu app. Você pode 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 drawable 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

Se você tiver criado seu projeto usando as Ferramentas do SDK Android (consulte Criar um projeto 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 mantêm 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 interface do usuário. 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 o seguinte 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 de idioma es e o código do país ES. O Android carrega os recursos apropriados de acordo com as configurações de localidade no dispositivo no momento da execução. Para mais informações, consulte Fornecimento de recursos alternativos.

Depois de decidir para quais localidades seu aplicativo oferecerá compatibilidade, crie os subdiretórios e arquivos de recursos. Por 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 cada atributo name do recurso.

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

Por 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 com a sintaxe @<resource type>/<resource name> sempre que o atributo XML aceitar um valor compatível.

Por 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 adequadas. 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 em uma direção RTL.

Na maioria das vezes, são os próprios apps que geram essas instâncias de texto de direção oposta incorporada. 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 insere 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 uma IU 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 interface 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 para a esquerda, como pretendido. 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 uma mensagem localizada. 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 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 a dispõe em caracteres de formatação Unicode que declaram essa direção. Como o "15" agora aparece dentro do texto declarado como LTR, ele é 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 (API de nível 18) ou versões posteriores, use a versão do BidiFormatter encontrada no framework do Android. Caso contrário, use a versão do BidiFormatter encontrada 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 da maneira apropriada 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 o 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 no app Config. e sua equivalente RTL:

A área de notificação está alinhada à direita, próxima ao canto superior direito; o botão de menu na barra do app 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 "Voltar" 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 do app 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 de retorno 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 ter em mente o seguinte:

  • O espelhamento de texto RTL só é compatível com apps quando usado em dispositivos com Android 4.2 (API de nível 17) ou posterior. 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 e convide pessoas que usam scripts RTL para usar seu 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 pode 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 apareça 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 forma:

build.gradle (Module: app)

    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 (API de nível 16) ou versões anteriores, o atributo android:supportsRtl será ignorado, assim como os valores start e end que aparecem nos arquivos de layout do app. Nesse caso, o espelhamento de layout RTL não acontecerá automaticamente no seu app.

Atualizar os recursos existentes

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

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

Para usar os recursos de alinhamento RTL da estrutura, 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 gerencia os atributos de alinhamento da IU com base na versão do SDK de destino, com a definição de left e right, bem como de start e end.

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
API de nível 17 ou posterior?
Esquerda e direita definidas? Início e término definidos? Resultado
Sim Sim Sim start e end resolvidos e modificação de left e right
Sim Sim Não Apenas left e right são usados
Sim Não Sim Apenas 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 Apenas 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 são adicionadas versões específicas dos seus arquivos de recursos de valor, layout e drawables que contenham valores personalizados para diferentes idiomas e direções de texto.

No Android 4.2 (API de nível 17) e em versões posteriores, 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 retroativa com o carregamento de recursos existentes anteriores, 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 seu diretório res/, como mostrado no exemplo a seguir:

    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, sua estrutura de 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 (API de nível 17), a maior parte dos elementos do framework de IU é 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 RTL, desde que os arquivos de manifesto correspondentes incluam o atributo android:supportsRtl="true".

Oferecer compatibilidade com apps legados

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

Para verificar se o layout precisa usar 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 versões posteriores das Ferramentas de criação do SDK Android.

Testar o uso de opções do desenvolvedor

Em dispositivos com Android 4.4 (API de nível 19) ou versões posteriores, é possível ativar 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 que precisam ser especificamente 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 alteração em qualquer propriedade relacionada a RTL, como direção de layout, parâmetros de layout, padding, direção do texto, alinhamento do texto ou posicionamento de drawable, use o callback onRtlPropertiesChanged(). Esse callback permite conseguir a direção do layout atual e atualizar objetos View de uma atividade da maneira adequada.

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 um diálogo ou um elemento correspondente, configure a direção correta do layout com base do 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 exigem maior consideração:

onMeasure()
As medidas de visualização podem variar, dependendo da direção do texto.
onLayout()
Se você criar o próprio layout de implementação, precisará chamar super() na sua versão de onLayout() e adaptar a lógica personalizada para que 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 em execução no dispositivo:

  • Em dispositivos com o Android 4.3 (API de nível 18) e versões anteriores, você precisa adicionar e definir arquivos de recurso -ldrtl.
  • No Android 4.4 (API de nível 19) e versões posteriores, você pode usar android:autoMirrored="true" ao definir seu drawable, o que permite ao sistema gerenciar 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 de todo o drawable. Se o drawable contém vários elementos ou se refleti-lo mudaria a interpretação dele, faça você mesmo o espelhamento. 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 o código existente que lida com valores alinhados à esquerda e à direita, mesmo que esteja usando start e end para seus valores de gravidade.

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

Margem e preenchimento

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

Veja também