c
Por Leandro Macedo
lmacedo@lmacedo.eti.br

Leandro Macedo coordena projetos .Net na Sofis Informática no RJ. É um Microsoft Certified Professional, graduado em Tecnologia da Informação e Pós Graduado em Tecnologia e Segurança de Banco de Dados. É membro ativo da comunidade DevAspNet, e pode ser facilmente encontrado na lista de discussão (br.groups.yahoo.com/group/devasPNET) e nas reuniões do grupo.

Aplicações web multi-idioma com a classe GlobalPage

No mundo globalizado de hoje, nada mais comum do que a necessidade de se oferecer suporte a múltiplos idiomas nos sites corporativos, visando o aumento das vendas, internacionalizando não só os portais como também os softwares comercializados por essas empresas.

Este artigo irá abordar a globalização de aplicações Asp.Net, passando por uma breve introdução, e chegando na implementação e uso da classe GlobalPage, com o objetivo de facilitar a internacionalização de aplicações web, aproximando-se das facilidades presentes nos projetos Windows Forms.

RFC 1766

Os idiomas (culturas) possuem uma padronização quanto a sua identificação, que deve ser seguida quando trabalhamos com internacionalização. O documento que rege essa especificação é a RFC 1766 (Tags for the Identification of Languages), que é uma junção da ISO 639 (Language Codes) com a ISO 3166 (Country Codes).

A cultura se refere a língua do usuário, opcionalmente combinada com a localização (país ou região). A língua deve ser representada por 2 letras minúsculas e a localização por duas letras maiúsculas, concatenadas com um "-" entre elas.

Alguns exemplos de culturas : pt (língua portuguesa), pt-BR (português do Brasil), pt-PT (português de Portugal), en-US (inglês dos Estados Unidos), es-AR (espanhol da Argentina), etc.

Nós podemos obter o nome de todas as culturas específicas utilizando a classe CultureInfo do namespace System.Globalization, como no exemplo abaixo:

        Dim Culturas() As System.Globalization.CultureInfo

        Culturas = System.Globalization.CultureInfo.GetCultures(Globalization.CultureTypes.SpecificCultures)

 

        For Each Cultura As System.Globalization.CultureInfo In Culturas

            Console.WriteLine(Cultura.Name)

        Next

Arquivos de Recurso

Recurso é qualquer dado não executável, que é distribuído com uma aplicação. Armazenar os dados em arquivos de recurso, nos permite mudar esses dados sem a necessidade de alterar a aplicação.

Arquivos de recurso são importantes peças no trabalho de globalização, já que se encaixam perfeitamente bem em mensagens de erro e críticas, texto de objetos da interface, imagens, etc.

No .Net esses arquivos estão presentes em todo lado, basta pedir para exibir os arquivos ocultos em nossos projetos windows ou web, que veremos diversos arquivos com a extensão ".resx" dando flexibilidade a diversos mecanismos do .Net.

Um ponto importante a destacar, é que esses arquivos ".resx" são nada mais do que arquivos xml. Pronto; falou-se em flexibilidade, chegamos no xml, nada mais usual que ele para esse tipo de coisa !

Globalização em Windows Forms

Quem já teve a oportunidade de trabalhar com globalização em aplicações Windows Forms, percebeu o quanto essa tarefa é fácil de ser realizada, basta trocar a propriedade "Localizable" do form para "True", preencher o "Text" dos objetos com os valores default, e depois ir trocando a propriedade "Language" e ir preenchendo novamente o "Text" dos controles. Por último basta mudar a cultura da Thread atual antes de exibir o form, que lá estarão os controles traduzidos perfeitamente, como em um passe de mágica !

Como isso está funcionando por "debaixo dos panos" ? Simples, 1 arquivo ".resx" é criado para cada "Language" utilizada no form, e lá ficam armazenados os textos dos controles em cada cultura a ser trabalhada, e automaticamente o framework se encarrega de ler o arquivo respectivo a cultura da thread corrente em tempo de execução, e preencher os controles com os valores presentes no xml.

Infelizmente não existe esse mecanismo facilitador nos Web Forms, o que nos leva a pegar uma "inspiração" na forma de trabalho dos Windows Forms, e a tentar criar algo próximo na Web, que nos leve a menos trabalho para internacionalizar nossas aplicações Web !

Iniciando a aplicação de exemplo

Vamos criar uma nova aplicação Web, e chamar de GlobalDemo, nela vamos criar uma página de login (login.aspx) e um formulário de cadastro de usuários (cadastro.aspx); criaremos também um user control chamado menu.ascx que será o menu do nosso site.

Vejam abaixo o código inicial (layout) desses 3 arquivos, não esquecendo de dar bastante importância ao nome(id) dos controles pois eles serão utilizados posteriormente para referenciá-los nos arquivos de recurso. É importante salientar também, que a propriedade "Text" deverá permanecer em branco.

menu.ascx

<%@ Control Language="vb" AutoEventWireup="false" Codebehind="menu.ascx.vb" Inherits="GlobalDemo.menu" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>

<asp:HyperLink id="lnk_login" runat="server" NavigateUrl="login.aspx"></asp:HyperLink>

&nbsp; -&nbsp;

<asp:HyperLink id="lnk_cadastro" runat="server" NavigateUrl="cadastro.aspx"></asp:HyperLink>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<asp:LinkButton id="cmd_portugues" runat="server" CausesValidation="False">Português</asp:LinkButton>&nbsp;&nbsp;

<asp:LinkButton id="cmd_ingles" runat="server" CausesValidation="False">English</asp:LinkButton>&nbsp;&nbsp;

<asp:LinkButton id="cmd_espanhol" runat="server" CausesValidation="False">Español</asp:LinkButton>


login.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="login.aspx.vb" Inherits="GlobalDemo.login"%>

<%@ Register TagPrefix="uc1" TagName="menu" Src="menu.ascx" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

    <HEAD>

        <title>login</title>

        <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">

        <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">

        <meta name="vs_defaultClientScript" content="JavaScript">

        <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">

    </HEAD>

    <body>

        <form id="Form1" method="post" runat="server">

            <uc1:menu id="Menu1" runat="server"></uc1:menu><BR>

            <BR>

            <BR>

            <BR>

            <BR>

            <asp:Label id="lbl_login" runat="server"></asp:Label><BR>

            <asp:TextBox id="txt_email" runat="server"></asp:TextBox>

            <asp:RegularExpressionValidator id="valid_login" runat="server" ControlToValidate="txt_email" ValidationExpression="\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">*</asp:RegularExpressionValidator><BR>

            <BR>

            <asp:Label id="lbl_senha" runat="server"></asp:Label><BR>

            <asp:TextBox id="txt_senha" runat="server" TextMode="Password"></asp:TextBox>

            <asp:RequiredFieldValidator id="valid_senha" runat="server" ControlToValidate="txt_senha">*</asp:RequiredFieldValidator><BR>

            <BR>

            <asp:Button id="cmd_ok" runat="server"></asp:Button>

            <asp:ValidationSummary id="ValidationSummary1" runat="server" ShowMessageBox="True" ShowSummary="False"></asp:ValidationSummary>

        </form>

    </body>

</HTML>

cadastro.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="cadastro.aspx.vb" Inherits="GlobalDemo.cadastro"%>

<%@ Register TagPrefix="uc1" TagName="menu" Src="menu.ascx" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

    <HEAD>

        <title>cadastro</title>

        <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">

        <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">

        <meta name="vs_defaultClientScript" content="JavaScript">

        <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">

    </HEAD>

    <body>

        <form id="Form1" method="post" runat="server">

            <uc1:menu id="Menu1" runat="server"></uc1:menu><BR>

            <BR>

            <BR>

            <asp:Label id="lbl_primeiro_nome" runat="server"></asp:Label>&nbsp;

            <asp:TextBox id="txt_nome" runat="server"></asp:TextBox><BR>

            <BR>

            <asp:Label id="lbl_ultimo_nome" runat="server"></asp:Label>&nbsp;

            <asp:TextBox id="txt_sobrenome" runat="server"></asp:TextBox><BR>

            <BR>

            <asp:Label id="lbl_email" runat="server"></asp:Label>&nbsp;

            <asp:TextBox id="txt_email" runat="server"></asp:TextBox><BR>

            <BR>

            <asp:Label id="lbl_senha" runat="server"></asp:Label>&nbsp;

            <asp:TextBox id="txt_senha" runat="server"></asp:TextBox><BR>

            <BR>

            <asp:Label id="lbl_confirmar" runat="server"></asp:Label>&nbsp;

            <asp:TextBox id="txt_confirmar" runat="server"></asp:TextBox><BR>

            <BR>

            <asp:Button id="cmd_ok" runat="server"></asp:Button>

        </form>

    </body>

</HTML>

Criando os arquivos de recurso

Por que quando criamos as páginas não preenchemos a propriedade "Text" dos controles ? Simplesmente porque elas serão multi-língua, ou seja, seu conteúdo não estará fixo nos controles, mas sim dentro dos arquivos de recurso de cada cultura.

Nesse exemplo iremos trabalhar com 3 linguagens neutras, sem se prender a localização específica, serão elas o Português (pt), Inglês (en) e o Espanhol (es).

Para criar os arquivos ".resx" basta clicar com o botão direito do mouse sobre o projeto web, e pedir para adicionar um novo item, selecionando "assembly resource file". Daremos a eles os nomes de textos.pt.resx, textos.en.resx e textos.es.resx respectivamente.

Para preencher os arquivos basta clicar 2x sobre eles, e em "name" digitar o "nome da página ou user control" + "." + "id do controle", e em "value" digitar o valor que queremos que apareça no "Text" dos controles traduzido para cada idioma.

A visualização em modo XML dos 3 arquivos ficaria assim:


textos.pt.resx

<root>

    <data name="menu.lnk_login">

        <value>Entrar</value>

    </data>

    <data name="menu.lnk_cadastro">

        <value>Cadastrar-se</value>

    </data>

    <data name="login.lbl_login">

        <value>Endereço eletrônico</value>

    </data>

    <data name="login.lbl_senha">

        <value>Senha</value>

    </data>

    <data name="login.cmd_ok">

        <value>Entrar</value>

    </data>

    <data name="login.valid_login">

        <value>Email inválido</value>

    </data>

    <data name="login.valid_senha">

        <value>Informe a senha</value>

    </data>

    <data name="cadastro.lbl_primeiro_nome">

        <value>Nome</value>

    </data>

    <data name="cadastro.lbl_ultimo_nome">

        <value>Sobrenome</value>

    </data>

    <data name="cadastro.lbl_email">

        <value>Endereço eletrônico</value>

    </data>

    <data name="cadastro.lbl_senha">

        <value>Senha</value>

    </data>

    <data name="cadastro.lbl_confirmar">

        <value>Confirmar senha</value>

    </data>

    <data name="cadastro.cmd_ok">

        <value>Enviar</value>

    </data>

</root>

textos.en.resx

<root>

    <data name="menu.lnk_login">

        <value>Sign In</value>

    </data>

    <data name="menu.lnk_cadastro">

        <value>Register</value>

    </data>

    <data name="login.lbl_login">

        <value>Email</value>

    </data>

    <data name="login.lbl_senha">

        <value>Password</value>

    </data>

    <data name="login.cmd_ok">

        <value>Login</value>

    </data>

    <data name="login.valid_login">

        <value>Invalid Email</value>

    </data>

    <data name="login.valid_senha">

        <value>Password is required</value>

    </data>

    <data name="cadastro.lbl_primeiro_nome">

        <value>First name</value>

    </data>

    <data name="cadastro.lbl_ultimo_nome">

        <value>Last name</value>

    </data>

    <data name="cadastro.lbl_email">

        <value>Email</value>

    </data>

    <data name="cadastro.lbl_senha">

        <value>Password</value>

    </data>

    <data name="cadastro.lbl_confirmar">

        <value>Repeat Password</value>

    </data>

    <data name="cadastro.cmd_ok">

        <value>Send</value>

    </data>

</root>


textos.es.resx

<root>

    <data name="menu.lnk_login">

        <value>Iniciar sesión</value>

    </data>

    <data name="menu.lnk_cadastro">

        <value>Registrar</value>

    </data>

    <data name="login.lbl_login">

        <value>Mail</value>

    </data>

    <data name="login.lbl_senha">

        <value>Contraseña</value>

    </data>

    <data name="login.cmd_ok">

        <value>Entrar</value>

    </data>

    <data name="login.valid_login">

        <value>Informar mail valido</value>

    </data>

    <data name="login.valid_senha">

        <value>Informar contraseña</value>

    </data>

    <data name="cadastro.lbl_primeiro_nome">

        <value>Nombre</value>

    </data>

    <data name="cadastro.lbl_ultimo_nome">

        <value>Último nombre</value>

    </data>

    <data name="cadastro.lbl_email">

        <value>Mail</value>

    </data>

    <data name="cadastro.lbl_senha">

        <value>Contraseña</value>

    </data>

    <data name="cadastro.lbl_confirmar">

        <value>Confirmar Contraseña</value>

    </data>

    <data name="cadastro.cmd_ok">

        <value>Envia</value>

    </data>

</root>

Caso eu precisasse incluir imagens (ícones, bitmaps...) no meu arquivo de recursos, eu poderia utilizar o aplicativo ResEditor, que pode ser encontrado (com código fonte) dentro da pasta "Tutorials" do Framework SDK, e que facilita bastante o trabalho.

Criando a classe GlobalPage

O objetivo agora é criar uma classe que leia os arquivos de recurso e preencha o "Text" dos controles automaticamente, bastando apenas trocar a herança de nossas páginas de System.Web.UI.Page para GlobalPage.

A classe também deverá manter os arquivos de recurso em Cache, invalidando-o caso ocorram mudanças nos arquivos ".resx" de origem.

Vamos então ao código da classe !

Declaração da classe herdando de Page, e algumas de suas principais propriedades privadas.

Public Class GlobalPage

    Inherits System.Web.UI.Page

 

    'propriedade que indica se devemos ou não globalizar a aplicação (cadastrada no web.config)

    Private Localizable As String = ConfigurationSettings.AppSettings("Localizable")

 

    'coleção com os objetos ao qual a classe oferece suporte a globalização

    Private aObjetos As System.Collections.Specialized.StringCollection

 

    'nomes dos recursos e valores, extraidos do arquivo ".resx" e mantido em cache

    Private Recurso As NameObject

 

    'nome da pagina atual para localizar nos recursos

    Private ScriptName As String

Função que retorna o nome da página.

    Public Function PageName() As String

        Dim script As String = System.IO.Path.GetFileName(Request.FilePath)

        script = script.Substring(0, script.Length - 5).Trim.ToLower

        Return script

    End Function

No OnLoad, chamamos método que verifica necessidade de globalizar, e inicializa a sessão.

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)

        init_global()

        MyBase.OnLoad(e)

    End Sub

 

    Private Sub init_global()

 

        'se não cadastrado no web.config entende-se que não trabalharemos com globalização

        If IsNothing(Localizable) Then

            Localizable = "false"

        End If

 

        'se estamos trabalhando com globalização

        If Localizable = "true" Then

 

            'cria a variavel de sessão que indica a cultura atual

            'lendo do web.config ou do Client

            If IsNothing(Session("language")) Then

                Dim landefa As String = ConfigurationSettings.AppSettings("DefaultLanguage")

                If IsNothing(landefa) Then

                    landefa = DefaultLanguage()

                Else

                    landefa = IIf(landefa.Trim.Length > 1, landefa.Trim, DefaultLanguage())

                End If

                Dim lan2 As String = landefa.Substring(0, 2).Trim.ToLower

                If (Not lan2 = "pt") AndAlso (Not lan2 = "en") AndAlso (Not lan2 = "es") Then

                    Session("language") = "pt-BR"

                Else

                    Session("language") = landefa

                End If

            End If

 

            'chama metodos que realizam a globalização

            muda_thread()

            muda_objetos()

 

        End If

 

    End Sub

 

    'função que obtem a linguagem do Client

    Private Function DefaultLanguage() As String

        Dim sLan As String = ""

        Try

            Dim aLan As String() = Request.UserLanguages()

            sLan = aLan(0)

            If sLan.Trim.Length > 2 Then

                sLan = sLan.Substring(0, 3) + sLan.Substring(3, 2).Trim.ToUpper

            End If

        Catch ex As Exception

        End Try

 

        Return sLan

    End Function

Método que muda a cultura da Thread corrente.

    Private Sub muda_thread()

        Dim Lingua As String = Session("language")

        Dim cultura As New CultureInfo(Lingua)

        System.Threading.Thread.CurrentThread.CurrentUICulture = cultura

        System.Threading.Thread.CurrentThread.CurrentCulture = cultura

    End Sub

Método que chama o carregamento do arquivo de recursos, e localiza o HtmlForm na coleção de controles da página para globalizar.

    Private Sub muda_objetos()

 

        init_resource()

 

        ScriptName = PageName()

 

        If Page.HasControls Then

            For Each oControl As Control In Page.Controls

                If oControl.GetType.Name = "HtmlForm" Then

                    Globaliza(oControl.Controls)

                End If

            Next

        End If

 

    End Sub

Método que carrega o arquivo de recursos para o Cache, caso necessário.

    Private Sub init_resource()

 

        'nome do cache que irá armazenar o recurso para essa cultura

        Dim cache_name As String = "recurso_" & Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName

 

        'caso essa cultura ainda não esteja no cache

        If IsNothing(Cache.Item(cache_name)) Then

 

            'obtem nome base do arquivo de recursos do web.config

            Dim file_name As String = ConfigurationSettings.AppSettings("ResFile")

            If IsNothing(file_name) Then

                file_name = "recursos"

            End If

 

            'obtem nome e caminho fisico do arquivo

            file_name = AppDomain.CurrentDomain.BaseDirectory.Replace("/", "\").Trim & file_name.Trim & "." & Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName & ".resx"

 

            'instancia o ResourceReader responsavel por carregar o ".resx"

            Dim oRM As New ResXResourceReader(file_name.Trim)

 

            Dim oID As IDictionaryEnumerator = oRM.GetEnumerator

            Dim oList As New Collections.Specialized.ListDictionary

 

            'preenche um dicionário que será usado de base

            'para a coleção de nomes e objetos

            For Each dd As DictionaryEntry In oRM

                oList.Add(dd.Key, dd.Value)

            Next

 

            'instancia a nova coleção com os recursos dessa cultura

            Recurso = New NameObject(oList, True)

 

            'insere coleção no Cache com dependência para o arquivo

            Cache.Insert(cache_name, Recurso, New Caching.CacheDependency(file_name))

 

        Else

 

            'o arquivo dessa cultura ja está carregado no cache

            'então basta ler ele para a propriedade Recurso

            Recurso = DirectCast(Cache.Item(cache_name), NameObject)

 

        End If

 

    End Sub

Função que verifica se o objeto possui suporte a tradução na classe.

    Private Function objeto_valido(ByVal objname As String) As Boolean

        If IsNothing(aObjetos) Then

            aObjetos = New System.Collections.Specialized.StringCollection

            aObjetos.Add("Label")

            aObjetos.Add("Button")

            aObjetos.Add("LinkButton")

            aObjetos.Add("ImageButton")

            aObjetos.Add("HyperLink")

            aObjetos.Add("CheckBox")

            aObjetos.Add("RadioButton")

            aObjetos.Add("RequiredFieldValidator")

            aObjetos.Add("CompareValidator")

            aObjetos.Add("RegularExpressionValidator")

            aObjetos.Add("CustomValidator")

            aObjetos.Add("HtmlGenericControl")

            aObjetos.Add("HtmlInputButton")

        End If

        Return aObjetos.Contains(objname)

    End Function

Funções protegidas e privadas que obtem strings e objetos dos Recursos.

    Protected Function MsText(ByVal str As String) As String

        muda_thread()

        init_resource()

        Return FindValue(str)

    End Function

 

    Protected Function MsObject(ByVal str As String) As Object

        muda_thread()

        init_resource()

        Return FindObject(str)

    End Function

 

    Private Function FindValue(ByVal KeyText As String) As String

        Return Recurso(KeyText)

    End Function

 

    Private Function FindObject(ByVal KeyText As String) As Object

        Return Recurso(KeyText)

    End Function

Método que efetiva a globalização dos controles.

    Private Sub Globaliza(ByVal oControls As ControlCollection, Optional ByVal Uc As String = Nothing)

        For Each oFormControl As Control In oControls

            If IsChild(oFormControl, "System.Web.UI.UserControl") Then

                If Not IsNothing(oFormControl.Controls) Then

                    Globaliza(oFormControl.Controls, oFormControl.GetType.BaseType.Name.Trim)

                End If

            Else

                Dim str_text As String = Nothing

                Dim str_find As String = ""

                If Not IsNothing(oFormControl.ID) Then

                    If IsNothing(Uc) Then

                        str_find = ScriptName & "." & oFormControl.ID.Trim.ToLower

                    Else

                        str_find = Uc & "." & oFormControl.ID.Trim.ToLower

                    End If

                End If

                If objeto_valido(oFormControl.GetType.Name) Then

                    str_text = FindValue(str_find)

                End If

                If (Not IsNothing(str_text)) AndAlso (Not str_text = "") Then

                    If oFormControl.GetType.Name = "Label" Then

                        CType(oFormControl, Label).Text = str_text

                    ElseIf oFormControl.GetType.Name = "Button" Then

                        CType(oFormControl, Button).Text = str_text

                    ElseIf oFormControl.GetType.Name = "LinkButton" Then

                        CType(oFormControl, LinkButton).Text = str_text

                    ElseIf oFormControl.GetType.Name = "ImageButton" Then

                        CType(oFormControl, ImageButton).AlternateText = str_text

                    ElseIf oFormControl.GetType.Name = "HyperLink" Then

                        CType(oFormControl, HyperLink).Text = str_text

                    ElseIf oFormControl.GetType.Name = "CheckBox" Then

                        CType(oFormControl, CheckBox).Text = str_text

                    ElseIf oFormControl.GetType.Name = "RadioButton" Then

                        CType(oFormControl, RadioButton).Text = str_text

                    ElseIf oFormControl.GetType.Name = "RequiredFieldValidator" Then

                        CType(oFormControl, RequiredFieldValidator).ErrorMessage = str_text

                    ElseIf oFormControl.GetType.Name = "CompareValidator" Then

                        CType(oFormControl, CompareValidator).ErrorMessage = str_text

                    ElseIf oFormControl.GetType.Name = "RangeValidator" Then

                        CType(oFormControl, RangeValidator).ErrorMessage = str_text

                    ElseIf oFormControl.GetType.Name = "RegularExpressionValidator" Then

                        CType(oFormControl, RegularExpressionValidator).ErrorMessage = str_text

                    ElseIf oFormControl.GetType.Name = "CustomValidator" Then

                        CType(oFormControl, CustomValidator).ErrorMessage = str_text

                    ElseIf oFormControl.GetType.Name = "HtmlGenericControl" Then

                        CType(oFormControl, HtmlGenericControl).InnerText = str_text

                    ElseIf oFormControl.GetType.Name = "HtmlInputButton" Then

                        CType(oFormControl, HtmlInputButton).Value = str_text

                    End If

                End If

            End If

        Next

    End Sub


Usando a classe GlobalPage


Primeiro devemos cadastrar no web.config as nossas 3 chaves de configuração já citadas acima.

  <appSettings>

       <add key="Localizable" value="true" />

       <add key="ResFile" value="textos" />

       <add key="DefaultLanguage" value="pt-BR" />

  </appSettings>


Vamos agora codificar os botões de mudança de língua presentes no user control menu.ascx.

    Private Sub cmd_portugues_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_portugues.Click

        CType(Me.Page, GlobalWeb.GlobalPage).muda_lingua("pt-BR")

    End Sub

 

    Private Sub cmd_ingles_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_ingles.Click

        CType(Me.Page, GlobalWeb.GlobalPage).muda_lingua("en-US")

    End Sub

 

    Private Sub cmd_espanhol_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd_espanhol.Click

        CType(Me.Page, GlobalWeb.GlobalPage).muda_lingua("es-AR")

    End Sub

Por último, basta mudar a herança de nossos web forms para GlobalPage, e pronto ! É só rodar, que a globalização estará implementada !

Caso fosse necessário ler um texto qualquer ou até mesmo um objeto serializado dentro do recurso, bastaria usar as funções agora presentes na página MsText(nome_recurso) ou MsObject(nome_recurso).

Conclusão

Neste artigo podemos ver um pouco sobre a necessidade de internacionalizar nossos produtos, e como o .net framework pode nos ajudar nesta tarefa, tornando-a bastante simples com o uso de classes personalizadas como a GlobalPage, e o uso de arquivos de recursos.

Mantenha-se antenado, pois a nova versão do ASP.Net (Whidbey) promete muitas novidades !

Links

- Aplicação de Exemplo + Código fonte da classe GlobalPage
- Meu blog: www.lmacedo.eti.br
- RFC 1766 : www.faqs.org/rfcs/rfc1766.html
- ISO 639 : www.w3.org/WAI/ER/IG/ert/iso639.htm
- ISO 3166 : www.w3.org/International/O-misc-iso3166.html
- MSDN : http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cptutorials/html/Resources_and_Localization_using_the__NET_Framework_SDK.asp
- Globalização no ASP.net 2.0 : http://msdn.microsoft.com/asp.net/whidbey/default.aspx?pull=/library/en-us/dnvs05/html/ASP2local.asp

Dicas para quem está começando:
Veja os próximos eventos
que você não pode perder :

22/11/2008 SQL Launch -
Linhares - ES
Por : devASPNet


22/11/2008 SQL Launch- SQL Server 2008
Rio Paranaiba - Viçosa - MG
Por : devASPNet


22/11/2008 SQL Launch - SQL Server 2008
Volta Redonda - RJ
Por : devASPNet


22/11/2008 SQL Launch- SQL Server 2008
Franca - SP
Por : devASPNet


22/11/2008 SQL Launch - SQL Server 2008
Canoinhas - SC
Por : devASPNet


22/11/2008 SQL Launch - SQL Server 2008
Tefé - AM
Por : devASPNet


25/11/2008 SQL Launch - SQL Server 2008
Rio de Janeiro - RJ
Por : devASPNet


27/11/2008 SQL Lauch- SQL Server 2008
São Paulo - SP
Por : devASPNet


28/11/2008 SQL Launch - SQL Server 2008
São Paulo - SP
Por : devASPNet


29/11/2008 SQL Launch- SQL Server 2008
Pedro Leopoldo - MG
Por : devASPNet