Produto & UXARTIGO

Internacionalizando (I18n) aplicações Flex

Em grandes projetos web uma das principais preocupações dos arquitetos é a internacionalização do projeto. Fazer com que os usuários possam acessar e utilizar a aplicação sem ter que se preocupar com com a língua é um dos principais pontos positivos para um projeto se destacar no mercado.Visando isso, os arquitetos da Adobe aperfeiçoaram o recurso de internacionalização do Flex 3, este recurso é chamado de ResourceBundle. Desta maneira ficou muito simples e flexível aplicar I18n. Veja por que:

  1. Você pode compilar recursos para várias localidades em uma única aplicação ou módulo.
  2. Você acessar todos os recursos através do ResourceManager, no qual pode gerenciar os recursos para várias localidades. Você pode mudar em tempo de execução a localidade do recurso.
  3. Você também pode usar imagens, sons, etc, e não apenas Strings

Estes são apenas uns dos melhoramentos e vantagens de utilizar o ResourceBundle do Flex 3.

O ResourceBundle possui algumas exigências como criar um arquivo Locale para cada idioma, contudo existe uma forma de burlar isso utilizando arquivos properties. Neste artigo irei demonstrar como aplicar I18n usando o ResourceManager sem ter criar arquivos Locales.

I18n com ResourceBundle

Primeiramente, iremos criar um projeto Flex, irei chamá-lo de Flex_I18n_A. No projeto crie uma nova pasta chamada I18n, e nela crie os seguintes arquivos XML:
pt_BR, en_US e es_ES.

<?xml version="1.0" encoding="iso-8859-1"?><br /><locale lang="en_US"><br />       <property key="titulo_app" value="Applying I18n in Flex applications"/><br />       <property key="usuario" value="Username"/><br />       <property key="senha" value="Password"/><br />       <property key="login_erro" value="User credentials are wrong. Please try again"/><br />       <property key="bt_login" value="Login"/><br />       <property key="bt_logout" value="Logout"/><br />       <property key="msg_boas_vindas" value="User logged in successfully."/><br />       <property key="selecione_idioma" value="Select a language."/><br /></locale><br /><br /><locale lang="pt_BR"><br />       <property key="titulo_app" value="Aplicando I18n em aplicações Flex."/><br />       <property key="usuario" value="Usuário"/><br />       <property key="senha" value="Senha"/><br />       <property key="login_erro" value="Dados incorretos. Tente novamente. "/><br />       <property key="bt_login" value="Login"/><br />       <property key="bt_logout" value="Sair"/><br />       <property key="msg_boas_vindas" value="Usuário logado com sucesso ."/><br />       <property key="selecione_idioma" value="Selecione um idioma."/><br /></locale><br /><br /><locale lang="es_ES"><br />       <property key="titulo_app" value="Aplicando i18n aplicaciones en Flex."/><br />       <property key="usuario" value="Nombre de Usuario"/><br />       <property key="senha" value="Contraseña"/><br />       <property key="login_erro" value="Credenciales de usuario se equivoca. Por favor, inténtelo de nuevo."/><br />       <property key="bt_login" value="Inicio de sesión"/><br />       <property key="bt_logout" value="Cerrar sesión"/><br />       <property key="msg_boas_vindas" value="Usuario conectado correctamente."/><br />       <property key="selecione_idioma" value="Seleccione um idioma."/><br /></locale>

Com os três arquivos criados, iremos criar a classe que irá fazer o carregamento dos arquivos xml.

Para cada key do XML será setado na propriedade content do ResourceBundle, desta maneira poderemos usar o metodo getString para pegar o value internacionalizado da key.

Agora iremos criar a classe com/flexI18n/utils/ConfigI18n

<strong>;</strong><strong>package</strong> com.flexI18n.utils {<br /><br />     <strong>import</strong> flash.events.Event;<br /><br />     <strong>import</strong> flash.net.URLLoader;<br /><br />     <strong>import</strong> flash.net.URLRequest;<br /><br /><br /><br />     <strong>import</strong> mx.resources.ResourceBundle;<br /><br />     <strong>import</strong> mx.resources.ResourceManager;<br /><br /><br /><br />     /**<br /><br />     * @author Fabiel Prestes<br /><br />     */<br /><br />     <strong>public</strong> <strong>class</strong> ConfigI18n {<br /><br /><br /><br />          <strong>private</strong> <strong>var</strong> resourceBundle:ResourceBundle;<br /><br /><br /><br />          [<strong>Bindable</strong>]<br /><br />          <strong>public</strong> <strong>var</strong> currentLocale:String = "pt_BR";<br /><br /><br /><br />          <strong>public</strong> <strong>function</strong> ConfigI18n() {<br /><br />            <strong>super</strong>();<br /><br />          }<br /><br /><br /><br />          /**<br /><br />          * Carrega o xml locale baseado na String passada como parametro.<br /><br />          */<br /><br />          public <strong>function</strong> loadLocale(locale:String= "pt_BR"):<strong>void</strong> {<br /><br />               currentLocale = locale;<br /><br />               <strong>var</strong> xmlLoader:URLLoader = <strong>new</strong> URLLoader();<br /><br />               xmlLoader.addEventListener(Event.COMPLETE, createBundle);<br /><br />               xmlLoader.load(<strong>new</strong> URLRequest("I18n/" + currentLocale + ".xml"));<br /><br />          }<br /><br /><br /><br />          /**<br /><br />          * @private<br /><br />          * Apos o xml ser carregado com sucesso, deve-se pegar cada chave (key) e valor <br /><br />          * (value) e adicionar no resourceBundle, para assim ser acessado em qualquer local da aplicação.<br /><br />          * @eventType Event<br /><br />          */<br /><br />          <strong>private</strong> <strong>function</strong> createBundle(e:Event):<strong>void</strong> {<br /><br />               <strong>var</strong> propertyList:XMLList = <strong>new</strong> XML(e.target.data).property;<br /><br /><br /><br />               resourceBundle = <strong>new</strong> ResourceBundle(currentLocale, "ApplicationResource");<br /><br />               <strong>for each</strong> (<strong>var</strong> property:XML <strong>in</strong> propertyList) {<br /><br />                       resourceBundle.content[property.@key] = property.@value;<br /><br />                 }<br /><br /><br /><br />                 ResourceManager.getInstance().addResourceBundle(resourceBundle);<br /><br />                 ResourceManager.getInstance().update();<br /><br />                 ResourceManager.getInstance().localeChain = [currentLocale,"pt_BR"];<br /><br />           }<br /><br />       }<br /><br />}

Apenas uma observação: na classe ConfigI18n, nós criamos uma variável ResourceBundle, aplicamos todas as mensagens internacionalizadas do XML e por final pegamos nosso resource e aplicamos no ResourceManager, que é um singleton. Lembrando que sempre após adicionar um novo resource deve-se fazer um update no ResourceManager para que todas as alterações sejam efetivadas.

Outro detalhe é que quando XML terminou de ser carregado, foi instanciado o ResourceBundle e, assim, definido um nome para o Locale e um identificador para o BundleName. É através deste identificador que conseguiremos recuperar as mensagens internacionalizadas do ResourceManager.

OK, com a classe responsável por carregar e configurar o ResourceBundle, vamos criar uma tela de login para para demonstrar a utilização do ResourceBundle.

Agora iremos criar a classe com/flexI18n/views/LoginUI

<?xml version="1.0" encoding="utf-8"?><br /><strong><mx:TitleWindow</strong> xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="100%" height="100%"><br /><br />     <mx:Script><br />
        <![CDATA[
              <strong>import</strong> mx.controls.Alert;<br />              <strong>import</strong> mx.managers.PopUpManager;<br />              <strong>import</strong> com.flexI18n.utils.ConfigI18n;  <br />
               [<strong>Bindable</strong>] <br />
               <strong>public</strong> <strong>var</strong> availableLocales:Array = [ {label:"English", data:"en_US"},<br />                                                                        {label:"Portuguese", data:"pt_BR"},<br />                                                                        {label:"Spanish", data:"es_ES"}];<br /><br /><strong>          private</strong> <strong>function</strong> onLogin(e:Event):<strong>void</strong> {<br />                PopUpManager.removePopUp(<strong>this</strong>);<br />                Alert.show(resourceManager.getString(\\\'ApplicationResource\\\',\\\'msg_boas_vindas\\\'));<br />            }<br />
            <strong>private</strong> <strong>function</strong> comboChangeHandler():<strong>void</strong> {<br />                <strong>var</strong> locale:LocaleController = <strong>new</strong> LocaleController();<br />                locale.loadLocale(localeComboBox.selectedItem.data); <br />                <strong>this</strong>.localeComboBox.selectedIndex = -1;<br />            }  <br />
          ]]><br />
      </mx:Script><br />
      <mx:Form width="100%">
            <mx:FormItem label="{resourceManager.getString(\\\'ApplicationResource\\\',\\\'usuario\\\')}">
                 <mx:TextInput id="username" width="200"/>
            </mx:FormItem><br />
         <mx:FormItem label="{resourceManager.getString(\\\'ApplicationResource\\\',\\\'senha\\\')}" direction="horizontal">
                 <mx:TextInput id="password" displayAsPassword="true"  width="200"/>
            </mx:FormItem><br />
          <mx:FormItem label="{resourceManager.getString(\\\'ApplicationResource\\\',\\\'idioma\\\')}">
                 <mx:ComboBox id="localeComboBox" selectedIndex="-1"
                      dataProvider="{availableLocales}"
  change="comboChangeHandler()"<br />                       width="200" prompt="{resourceManager.getString(\\\'ApplicationResource\\\',\\\'selecione_idioma\\\')}"/><br />            </mx:FormItem><br />
    </mx:Form><br />
    <mx:ControlBar width="100%" horizontalAlign="right">
            <mx:Button label="{resourceManager.getString(\\\'ApplicationResource\\\',\\\'bt_login\\\')}"
            click="onLogin(event)" enabled="{username.text != \\\'\\\' &amp;&amp; password.text != \\\'\\\'}"  x="517" y="118"/><br />
     </mx:ControlBar><br /><br /></mx:TitleWindow>

Se vocês olharem a classe acima, verão que não foi, em nenhum momento, instanciado um objeto ResourceBundle. Mas, então, de onde saiu a variável resourceBundle usada? Muito simples: estamos usando o atributo da classe UIComponent.

A classe main fica da seguinte maneira:

<?xml version="1.0" encoding="utf-8"?><br /><mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="initApp()"><br /><br />
    <mx:Script><br />
        <![CDATA[<br />
            <strong>import</strong> mx.managers.PopUpManager;<br />
            <strong>import</strong> com.flexI18n.views.LoginUI;<br />
            <strong>import</strong> com.flexI18n.utils.LocaleController;<br /><br />
            <strong>private</strong> <strong>function</strong> initApp():<strong>void</strong>{<br />
                <strong>new</strong> ConfigI18n().loadLocale();<br /><br />
                <strong>var</strong> login:LoginUI = <strong>new</strong> LoginUI();<br />
                PopUpManager.addPopUp(login, <strong>this</strong>, <strong>true</strong>);<br />
                PopUpManager.centerPopUp(login);<br />
            }<br />
        ]]><br />
    </mx:Script><br /><br />
</mx:Application> 

É isso aí, uma maneira simples e fácil de fazer sua aplicação ficar multi-idioma.

Espero ter ajudado e até a próxima.

trabalha a desde 2005 em desenvolvimento e integração de sistemas WEB JEE. Começou a trabalhar com desenvolvimento de aplicações RIA Flex/Air em 2007. Atualmente é analista de sistemas pela JExperts, participa de fóruns e listas de discussões como Anddev, Adroid.org, GUJ, FlexDev, FloripaFlex e PortalAndroid.

Ver perfil