.NET - Compreendendo a Localização na plataforma .NET


Num mundo cada vez mais globalizado construir aplicações que tenham uma audiência internacional deixou de ser um diferencial para ser quase uma necessidade. E neste quesito o ideal seria construir e preparar a aplicação para lidar com isto desde o seu início, embora, isso nem sempre seja viável por questões de esforço e custo.

O objetivo principal do esforço de localização é a tradução dos recursos na interface do usuário e isso é recurso específico de cada aplicação que devido a grande variedade de culturas e idiomas deve decidir se adota ou não uma implementação genérica. Existem no entanto alguns elementos comuns de localização como o suporte a datas, formatos numéricos e de moeda que podem se implementados de forma genérica por classes da plataforma .NET.

A plataforma .NET realizou um esforço considerável para apoiar a internacionalização oferecendo uma API de suporte, controles de servidor e recursos do Visual Studio permitem que você realize a localização da sua aplicação de uma forma mais eficaz.

Neste artigo vou apresentar os conceitos e recursos básicos relacionados com a internacionalização na plataforma .NET.

Culturas e Regiões

Quando acessamos uma aplicação ASP .NET ela é executada sob uma cultura e sob uma configuração regional específicas que são definidas e configuradas no servidor onde roda a aplicação (este é o modo padrão de uma aplicação ASP .NET) ou em uma configuração aplicada pelo cliente quando requisitada.

O mundo é composto de uma infinidade de culturas, cada qual tem uma linguagem e um conjunto de formas definidas para tratar formatos números, utilizar moedas, realizar classificações, etc. A plataforma .NET define idiomas e regiões usando a definição padrão do RFC(Request for Comments) 1766 (http://www.ietf.org/rfc/rfc1766.txt), que especifica um idioma e região utilizando códigos de duas letras separadas por um traço.

A tabela a seguir fornece exemplos de algumas definições de cultura:

Código da Cultura Descrição
en-US English Language; United States
en-GB English Language; United Kingdom
en-AU English Language; Australia
en-CA English Language; Canada
fr-CA French Language; Canada

Os exemplos da tabela definem cinco culturas distintas que têm algumas semelhanças e algumas diferenças. Quatro das culturas falam a mesma língua (Inglês), assim o código da linguagem "en" é usado nestas configurações de cultura. Após a definição do idioma vem a definição da região.

Embora a maioria dessas culturas falem a mesma língua, é importante distingui-las definindo sua região (US para os Estados Unidos, GB para o Reino Unido, AU para a Austrália, e CA para o Canadá). Estas definições refletem o fato de que o Inglês usado nos Estados Unidos é um pouco diferente do Inglês usado no Reino Unido, e assim por diante.

Além da linguagem, existem diferenças em como as datas e os valores numéricos são representados. É por isso que a linguagem de uma cultura e região são apresentados em conjunto.

Muitos países contêm mais do que uma única língua, e cada um pode ter sua própria preferência para notação de datas e outros itens. Por exemplo, en-CA especifica os falantes de Inglês no Canadá. Porque o Canadá não é um único país de língua Inglês, inclui também a definição de cultura fr-CA para canadenses de língua francesa.

Esta definição de cultura é definida como especific culture (cultura específica) . Outro tipo de definição de cultura é a definição neutral culture e cada especific culture esta associada a uma neutral culture. Assim as culturas do idioma Inglês pertencem ao neutral culture EN.

Na figura ao lado temos como estes tipos de cultura estão relacionadas.

Quando o usuário final solicita uma página ASP.NET ou executa um Windows Forms, o item é executado em uma thread do pool de threads.

Esse segmento tem uma cultura associada a ele. Você pode obter informações sobre a cultura do segmento via programação e, em seguida, verificar se há detalhes específicos sobre essa cultura.

Para ver um exemplo de trabalhar com uma thread e ler as informações de cultura dessa thread, vamos iniciar com o o aplicativo Windows Forms básico.

Crie então um novo projeto no Visual Basic 2010 Express Edition do tipo Windows Forms Application chamado teste_LocalizacaoVB e inclua os controles button e textbox, a partir da ToolBox, no formulário form1.vb conforme o leiaute da figura abaixo:

Vamos usar os seguintes namespaces:

Imports System.Globalization
Imports System.Threading.Thread

Vamos definir a rotina chamada ExibirInformacaoCultura com o código a seguir:

 Private Sub ExibirInformacaoCultura()
        Dim ci As New CultureInfo(CurrentThread.CurrentCulture.ToString())
        TextBox1.Text = "CULTURA ATUAL : " & Environment.NewLine
        TextBox1.Text += "Nome        : " & ci.Name & Environment.NewLine
        TextBox1.Text += "Nome Pai    : " & ci.Parent.Name & Environment.NewLine
        TextBox1.Text += "Nome Exibido: " & ci.DisplayName & Environment.NewLine
        TextBox1.Text += "Nome Inglês : " & ci.EnglishName & Environment.NewLine
        TextBox1.Text += "Nome Nativo : " & ci.NativeName & Environment.NewLine
        TextBox1.Text += "Nome ISO    : " & ci.ThreeLetterISOLanguageName & Environment.NewLine
        TextBox1.Text += "Calendário tipo : " & ci.Calendar.ToString() & Environment.NewLine
    End Sub

No evento Click do botão de comando apenas chamamos a rotina conforme abaixo:

Private Sub btnExecutar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExecutar.Click
    ExibirInformacaoCultura()
End Sub

Executando o projeto iremos obter:

Neste exemplo criamos um objeto CultureInfo do namespace System.Globaliztion e atribuímos a cultura da thread atual chamando System.Threading.Thread.CurrentThread.CurrentCulture.ToString. Após preencher o objeto CultureInfo com cultura do usuário recuperamos mais informações sobre a cultura usando as propriedades do objeto conforme mostrado no formulário. A cultura atual padrão esta definida como pt-BR.

Além destas informações podemos usar o objeto CultureInfo para obter outras informações sobre a cultura bem como alterar a cultura da thread conforme exemplo a seguir, onde alteramos a cultura para es-ES (Espanhol):

Private Sub ExibirInformacaoCultura()
        'Dim ci As New CultureInfo(CurrentThread.CurrentCulture.ToString())

        CurrentThread.CurrentCulture = New Globalization.CultureInfo("es-ES")
        Dim ci As CultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture

        TextBox1.Text = "CULTURA ATUAL : " & Environment.NewLine
        TextBox1.Text += "Nome        : " & ci.Name & Environment.NewLine
        TextBox1.Text += "Nome Pai    : " & ci.Parent.Name & Environment.NewLine
        TextBox1.Text += "Nome Exibido: " & ci.DisplayName & Environment.NewLine
        TextBox1.Text += "Nome Inglês : " & ci.EnglishName & Environment.NewLine
        TextBox1.Text += "Nome Nativo : " & ci.NativeName & Environment.NewLine
        TextBox1.Text += "Nome ISO    : " & ci.ThreeLetterISOLanguageName & Environment.NewLine
        TextBox1.Text += "Calendário tipo : " & ci.Calendar.ToString() & Environment.NewLine
    End Sub

Você pode exibir todas as informações das culturas suportadas usando o código abaixo:

 Private Sub btnCulturas_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCulturas.Click
        Dim ci As CultureInfo
        For Each ci In CultureInfo.GetCultures(CultureTypes.NeutralCultures)
            lstCulturas.Items.Add(ci.Name & " - " & ci.DisplayName &" - " & ci.NativeName)
        Next
    End Sub

No código acima estou exibindo as informações em um controle ListBox(lstCulturas).

Em aplicações ASP .NET podemos também definir a cultura na aplicação por inteiro ou apensa em uma página específica usando as declarações de cultura do lado do servidor via arquivo de configuração. Para ilustrar este recurso vamos criar uma aplicação ASP .NET usando o Visual Web Developer 2010 Express Edition (VWD 2010).

Abra o VWD2010 e crie um novo projeto do tipo ASP .NET Web Empty Application com o nome Localizacao_ASPNET;

A seguir vamos incluir um novo Web Form selecionando o menu Project -> Add New Item e escolhendo o template Web Form definindo o nome Default.aspx e clicando no botão Add;

A seguir inclua na página um controle Calendar a partir da ToolBox e defina um texto conforme mostrado abaixo:

Executando o projeto iremos obter a exibição do calendário usando a cultura padrão pt-BR:

Podemos no entanto alterar o idioma padrão usado por este controle usando o arquivo Web.Config da aplicação e definindo outra opção conforme exemplo abaixo onde definimos a cultura para ru-RU (Russia)

<?xml version="1.0"?>

<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->

<configuration>
    <system.web>
        <compilation debug="true" strict="false" explicit="true" targetFramework="4.0" />
        <globalization culture="ru-RU" uiCulture="ru-RU" />
    </system.web>
</configuration>

Observe que bastou incluir a linha com a tag <globalization culture="ru-RU" uiCulture="ru-RU" /> no arquivo Web.Config para alterarmos a cultura padrão.

Observe os dois atributos usados: culture e uiCulture.

O atributo culture permite definir a cultura a ser usada para processar as solicitações de entrada, enquanto o atributo uiCulture permite que você defina a cultura padrão necessária para processar todos os arquivos de recursos na aplicação

Podemos também realizar a mesma alteração definindo a nível de página definindo os atributos na página Default.aspx:

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="Localizacao_ASPNET._Default" UICulture="ru-RU" Culture="ru-RU" %>

Além de usar as configurações do lado do servidor para definir a cultura para suas páginas ASP.NET, você também tem a opção de definir a cultura de acordo com o que o cliente tem definido como preferência em uma instância do Navegador.

Quando os usuários finais instalam o Microsoft Internet Explorer ou outro navegador, eles têm a opção de selecionar suas culturas preferidas em uma ordem específica (se tiverem selecionado mais do que uma preferência de cultura). Vejamos isso em ação no IE. No IE selecione Ferramentas => Opções da Internet

A seguir na aba Geral clique no botão Idiomas para ver a cultura padrão e no botão Adicionar para exibir a relação de idiomas disponíveis caso desejar alterar o padrão ou definir uma segunda cultura:

Após realizar as definições você pode ativar o reconhecimento automática da cultura em sua aplicação ASP .NET ao invés de especificar uma cultura específica no arquivo de configuração ou na página usando a palavra chave "auto" conforme código abaixo:

<%@ Page UICulture="auto" Culture="auto" %>

Dessa forma as datas, calendários e números na sua página irão ser exibidas na cultura definida como padrão ou preferida.

Além disso você pode também especificar na opção auto uma opção adicional definindo outra cultura de forma que se a ASP .NET não conseguir encontrar as configurações do usuário ela será usada. Exemplo: <%@ Page UICulture="auto:en-US" Culture="auto:en-US" %>

As datas merecem um cuidado especial visto que podem apresentar ambigüidades na interpretação.

Assim a data 08/11/2008 refere-se a oito de novembro de 2008 ou a onze de agosto de 2008 ?

Deve ser trabalho da camada de lógica de negócios ou da camada de apresentação converter as data e horários para utilização pelo usuário final. Para evitar erros de interpretação, devemos usar sempre a mesma cultura (ou cultura invariável) quando formos armazenar valores, tais como datas e horas, em um banco de dados ou outro tipo de armazenamento de dados.

A definição da configuração da cultura no nível do servidor em aplicações ASP.NET ou dentro de um aplicativo Windows Forms, como mostrado nos exemplos anteriores, permite ao seu aplicativo .NET fazer essas conversões para você.

Você também pode simplesmente atribuir uma nova cultura para o segmento em que o código está sendo executado.

Por exemplo, considere o exemplo a seguir onde definimos a exibição de datas para cada cultura no evento Click do botão de comando :

No formulário incluímos um controle TextBox (txtDataHorarios) e no um controle Button (btnExibirDatasPorCultura):


 Private Sub btnExibirDatasPorCultura_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) 
Handles btnExibir.Click
        Dim dt As DateTime = New DateTime(2010, 3, 2, 13, 5, 1, 10)
        Thread.CurrentThread.CurrentCulture = New CultureInfo("pt-br")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("es-mx")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("es-es")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("ru-RU")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("ar-SA")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("am-ET")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("as-IN")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("th-TH")
        txtDatasHorarios.Text += Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("zh-cn")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("zh-tw")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("ko-kr")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("zh-hk")
        txtDatasHorarios.Text +=
        Thread.CurrentThread.CurrentCulture.EnglishName & " : " & _
        dt.ToString() & Environment.NewLine
    End Sub

No exemplo estamos definindo a data 19/09/2011 e a hora 13:05:01:10.

Além de valores de data/hora, os números são exibidos de forma bastante diferente de uma cultura para outra.

Como pode um número ser representado de forma diferente em diferentes culturas ?

Bem, isso tem menos a ver com o número real (embora certas culturas usem diferentes símbolos para número) e mais a ver com a forma como os separadores para decimais em um número são usados para mostrar quantias como milhares, milhões,etc.

Por exemplo, na cultura English dos Estados Unidos (en-US), os números são representados da seguinte maneira: 6,123,456.00

Tomando este numero como exemplo, você pode ver que a cultura en-US usa uma vírgula como um separador de milhares e um ponto para indicar o início de quaisquer decimais que podem aparecer após o número. Isso pode ser muito diferente em outras culturas.

Abaixo temos um exemplo que mostra a representação de números em algumas culturas.

Definimos o controle Button (btnNumeros) e o controle TextBox (txtNumeros) no formulário form1.vb e definimos o código abaixo no evento Click do botão de comando:

 Private Sub btnNumeros_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) 
Handles btnNumeros.Click
        Dim nNumero As Double = 5123456.0
        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        txtNumeros.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumero.ToString("n") & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("vi-VN")
        txtNumeros.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumero.ToString("n") & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")
        txtNumeros.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumero.ToString("n") & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-CH")
        txtNumeros.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumero.ToString("n") & Environment.NewLine
    End Sub

Como você pode ver, as culturas mostram números em diferentes formatos.

No exemplo, a segunda cultura, vi-VN (vietnamitas no Vietnã), exibe um número de forma totalmente oposta a forma como ela é construída na cultura en-US. A cultura vietnamita usa pontos para os separadores de milhar e vírgula para decimais.(Na verdade este formato é muito comum em outras culturas inclusive a pt-BR)

A cultura fi-FI, Finlandês, usa espaços para os separadores de milhar e vírgula para o separador decimal, enquanto que a cultura fr-CH usa um apóstrofo para separar milhares, e um ponto para o separador decimal.

Outro cenário em que você representa números é quando se trabalha com moedas. Neste caso temos que nos preocupar em converter as moedas para que os usuários possam entender o valor apropriado de um item e também traduzir a construção da moeda como se fosse um número básico.

Cada cultura tem um símbolo de moeda distinto que é usado para significar que um número representado é uma moeda. Por exemplo, a cultura en-US representa a moeda no seguinte formato: $ 5,123,456.00

A cultura en-US usa um símbolo dólar dos EUA ($), e a localização deste símbolo é tão importante quanto o próprio símbolo. Para en-US, o símbolo $ precede diretamente o valor da moeda (sem espaço entre o símbolo e o primeiro caractere do número). Outras culturas usam símbolos diferentes para representar moeda e muitas vezes colocam os símbolos de moeda em diferentes posições da representação.

Vejamos outro exemplo onde vamos exibir o número representando o valor e o símbolo da moeda usada em alguns culturas.

Novamente incluímos um controle Button(btnExibeNumerosSimbolos) e um controle TextBox (txtNumerosSimbolo) onde definimos o código abaixo no evento CLick:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim nNumeroSimbolo As Double = 5123456.0
        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        btnNumerosSimbolo.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumeroSimbolo.ToString("c") & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("vi-VN")
        btnNumerosSimbolo.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumeroSimbolo.ToString("c") & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("fi-FI")
        btnNumerosSimbolo.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumeroSimbolo.ToString("c") & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-CH")
        btnNumerosSimbolo.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumeroSimbolo.ToString("c") & Environment.NewLine
        Thread.CurrentThread.CurrentCulture = New CultureInfo("pt-BR")
        btnNumerosSimbolo.Text += Thread.CurrentThread.CurrentCulture.EnglishName &
        " : " & nNumeroSimbolo.ToString("c") & Environment.NewLine
    End Sub

Observe que temos também símbolos diferentes para cada cultura.

Quando você estiver usando moedas em uma página ASP.NET se você tiver definido uma configuração automática de cultura para a página como um todo (definindo a cultura na direta @Page) você vai precisar definir uma cultura especifica para a moeda que é a mesma em todos os casos. Ao contrário de datas, para o qual as diferenças são principalmente a exibição, com uma moeda há uma expectativa de conversão de valores.

Assim se você estiver definindo uma moeda como R$ (Real) para exibir os seus valores não vai querer que outro símbolo de moeda seja exibido quando outras informações em outra cultura estiverem sendo exibidas (a não ser que você converta o valor de Real para a moeda da cultura). Neste caso se você estiver usando uma configuração de cultura automática (auto) você pode usar o seguinte código para evitar o problema:

Dim Numero As Double = 5123456.00
Dim ptMoeda As CultureInfo = New CultureInfo("pt-BR")
Response.Write(Numero.ToString("c", ptMoeda))

Se você esta pensando que todas as culturas realizam a ordenação de strings da mesma forma esta enganado. De forma geral isso é verdade mas existem diferenças.

Veja o exemplo a seguir onde temos em um formulário um botão de comando (btnOrdenar) e um controle TextBox (txtOrdem) que chama a rotina de ordenação abaixo:

Obs: Você tem que referenciar os namespaces System.Collections e System.Collections.Generic

Private Sub Ordenar()
         CurrentThread.CurrentCulture = New CultureInfo("en-US")
        'CurrentThread.CurrentCulture = New CultureInfo("fi-FI")
        Dim mLista As List(Of String) = New List(Of String)
        mLista.Add("Washington D.C.")
        mLista.Add("Helsinki")
        mLista.Add("Moscow")
        mLista.Add("Warsaw")
        mLista.Add("Vienna")
        mLista.Add("Tokyo")
        mLista.Sort()
        For Each item As String In mLista
            txtOrdem.Text += item.ToString() & Environment.NewLine
        Next
    End Sub
Código para ordenação de strings CurrentThread.CurrentCulture = New CultureInfo("en-US") CurrentThread.CurrentCulture = New CultureInfo("fi-FI")

A ordenação é realizada conforme a ordenação é feita pela cultura definida na thread atual na qual a aplicação esta rodando.

Comparando os resultados obtidos quando usamos a cultura en-US com a cultura fi-FI notamos que existem diferenças no resultado da ordenação.

Na cultura fi-FI Vienna esta ordenada de forma diferente e o motivo é que no idioma Finlandês não existe diferente entre a letra V e a letra W por isso Wa vem antes de Vi.

Pegue o projeto completo aqui: Teste_LocalizacaoVB.zip

"E estes sinais seguirão os que crerem: Em meu nome expulsarão os demônios; falarão novas línguas; Pegarão nas serpentes; e, se beberem alguma coisa mortífera, não lhes fará dano algum; e porão as mãos sobre os enfermos, e os curarão." Marcos 16:17-18

Referências:


José Carlos Macoratti