Entre para a LISTA VIP da Black Friday

00

DIAS

00

HORAS

00

MIN

00

SEG

Clique para saber mais
Alura > Cursos de Mobile > Cursos de Flutter > Conteúdos de Flutter > Primeiras aulas do curso Flutter i18n: entenda abordagens de internacionalização

Flutter i18n: entenda abordagens de internacionalização

Componentes de Internacionalização - Apresentação

Boas-vindas a mais um curso de Flutter! Nesse caso, vamos utilizar bloc para implementar a internacionalização do nosso projeto. O foco é discutir internacionalização, duas abordagens para internacionalização. É claro que você pode utilizar o mesmo conceito para localização de data, de moeda, etc. Só que nesse caminho de implementar a internacionalização no aplicativo que já temos do curso anterior, onde temos essa tela, para internacionalizar essas mensagens, vamos passar por várias questões do bloc, na prática.

Com telas fazendo carregamento dinâmico, fazendo cache local, fazendo um carregamento guloso, ou preguiçoso, eager ou lazy, e vamos discutir tudo isso enquanto implementamos um framework que vamos criar para facilitar a internacionalização.

A sacada é que no final de tudo isso, nosso código organizado vai ter para nós um padrão. Tenho um container, que é quem cria minha tela, tenho minha classe de internacionalização, seja de uma forma eager ou de uma forma lazy, essa forma eager está programática. Essa forma lazy se baseia em arquivos armazenados em um servidor remoto, e vamos até discutir como armazenar isso de uma maneira super prática e legal que quero que vocês mesmos criem e façam sem sofrimento.

E a padronização no gerenciamento desse estado, criação desse componente de localização de diversas. Então vamos passar por tudo isso e discutir tudo isso à medida em que implementamos essa internacionalização lazy, que acho que é super legal para que você aprenda não só a fazer internacionalização, aprenda a fazer ela de uma forma genérica o suficiente para o seu projeto.

Talvez uma forma mais simples, talvez uma forma mais avançada. Claro que dá para avançar mais ainda, controlar versionamento, controlar várias outras coisas.

Mas também acabamos vendo todas essas outras questões do flutter, do bloc, gerenciamento de estado do lazy, da organização, de criação desse framework, de frameworks que podemos extrair em outros pedaços para facilitar no dia a dia dos nossos projetos. Tudo isso vamos ver nesse curso, espero que você goste bastante e vamos lá.

Componentes de Internacionalização - Rodando o projeto

Vamos começar nosso curso, lembrando que o primeiro passo é baixarmos o projeto como ele estava no curso anterior, na Alura Cursos GitHub, flutter bloc. Nas atividades passamos um link. Aqui dentro do flutter bloc você vai clicar em code e download ZIP. Vai baixar esse ZIP.

Lembrando que isso é o front-end mobile. Além disso, queremos um server cient, certo? A parte que tem o back-end que executa a lógica dos clientes, das transações. Esse código está no ByteBank menos API. E você pode descer e clicar para baixar o runable.zIP.

Depois de você baixar esses dois arquivos você vai ter os dois arquivos aqui. Descompacte eles. ByteBank API vou descompactar e o flutter bloc master, que é o master do meu GitHub, vou clicar na direita, extract here também.

Então ByteBank API runable é onde tenho um arquivo jar, que é o servidor. E o flutter bloc master tenho todos os vídeos relativos às aulas do curso. Quero só esse aqui. Vou dar um copy da última aula, final da última aula. Ou você pode seguir um link para o começo desse curso, um ZIP com o começo desse curso. Eu preferi por esse caminho sempre.

Copiei e colei aqui, só vou renomear para "flutter-bloc2", onde vamos fazer a parte de internacionalização, entre outras coisas. Coloquei aqui bloc2, vou no meu Android studio, abrir um projeto já existente. Abro essa janela de diálogo do diretório, escolho o diretório onde está esse projeto "flutter-bloc2" e dou ok.

Ele vai abrir o projeto. Tenho que configurar também, se é a primeira vez que você está nessa máquina, o diretório do seu flutter. No meu caso já baixei o flutter. Se você já executou algum outro curso em flutter, já está utilizando o flutter, afinal esse é um curso mais avançado, então você vai em settings, procura aí flutter, em flutter você vai escolher o diretório do SDK.

O meu diretório do SDK está em usuário, Guilherme, documents, flutter. Maravilha. Então aqui dentro vou dar um apply, ele já detectou o flutter 1.23 na minha versão. Se eu abrir agora o meu lib main dart, posso dar um get dependences para ele baixar as dependências. Esses erros deveriam em algum momento serem fixados.

Ele está indexando, fazendo as coisas, etc. E posso abrir aqui meu SDK, o gerenciador de SDK. Esse projeto está configurado com a versão 28 do Android, então baixe a versão 28, deu um ok. No meu caso estou com a 28. Você pode também atualizar para 30, seja lá o que for. Nós estamos rodando a 28.

Seleciona, dá um ok para baixar ela, e cria, claro, um dispositivo. No AVD manager tenho um dispositivo que criei, que é um pixel 3, vou dar um play nele, não tenho o AVD explícito aqui, então ele vai rodar o emulador, estou bem assim. Ele tem o emulador rodando aqui e consigo agora dar um play, nesse emulador dar um play do main.dart e ver o que acontece.

Ele está buildando, vai demorar um pouco para buildar a primeira vez, enquanto isso vou em terminal, então não é no run, vou no terminal, vou no diretório do meu ByteBank, dou um dir, tenho server.jar, Java-jar server.jar, então se você ainda não fez, mas você fez os outros cursos anteriores, baixe o jar, baixei aqui a versão 14, pode baixar um mais recente também, Java-jar server.jar, ele vai rodar o server side que armazena as transferências, os usuários, etc.

Vou no meu Android, estou esperando ele buildar. Terminou, volto para o meu run, no meu run está buildando ainda, quando terminar de buildar vai instalar a APK para nós nesse celular, inicializar a APK e temos o projeto rodando. Podemos ver três funcionalidades. Welcome, Guilherme. Posso mudar para Guilherme Silveira. Lembrando, isso é um armazenamento em memória local.

Então, quando eu fechar, isso aqui vai ser perdido. Não fizemos um armazenamento persistente. Mas a lista de pessoas é um armazenamento persistente. Se eu colocar que eu gostaria de ter a Ana Stella com account number 3344, criei aqui, posso vir aqui, fechar meu aplicativo, mandar rodar de novo, estou mandando buildar de novo só para termos certeza.

Fechou, abriu, tudo mais, vamos ver que a Ana ainda vai estar lá, porque é o armazenamento persistente. Vamos lá em transferência, Ana Stella ainda está lá. Se eu clicar nela, posso colocar um valor de cem reais, pedir para transferir, a senha padrão é a senha 1000, vou confirmar a senha padrão, enviando.

Demorou demais, deu timeout. Por que será que deu timeout? Vamos dar uma olhada? Lembra, quando estamos fazendo as requisições http temos que ter a uri do nosso servidor. E a sua máquina vai ter um IP específico. Não sei qual o IP seu. Vou abrir um terminal novo, IP configure, if configure, seja lá o que for, para você pegar seu IP.

No meu caso, o IP é 8686226. Vou rodar, eu podia dar um hot reload, eu acho que dei um hot restart. Foi um run, cliquei em um run grosseiro. Então vamos lá. Nas listas de transferência para a Ana Stella, cem reais, a senha é 1000, agora usando o IP do 226, e agora então se eu voltar para o transaction feed tenho lá que transferi cem reais para a conta 3344.

Quero que você replique esse processo na sua máquina e começamos a desenvolver nosso código a partir daqui.

Componentes de Internacionalização - Internacionalização

Agora que temos o projeto rodando, nosso primeiro passo é conseguir de alguma maneira disponibilizar um componente que lida com a internacionalização e localização. Claro, você pode separar isso em dois componentes, eu vou chamar isso no meu container de localização.

Vamos criar nosso container de localização. Ao invés de ir para o meu dashboardContainer, vou ter meu localizationContainer. Meu localizationContainer vai trabalhar com internacionalização e localização, as duas coisas, e vou passar quem é o filho do meu localizationContainer, porque no meu caso, do meu aplicativo, vai ser meu dashboardContainer.

Mas se eu estiver em outro aplicativo e eu extraio esse localizationContainer para uma biblioteca, deixo isso como uma variável. Vou deixar dessa forma para ficar variável, vamos criar o localizationContainer, então create class localizationContainer, é claro, é um statelessWidget. E lembra, quando ele é um statelessWidget fizemos algumas coisas com os containers.

Um blocContainer já estende um statelessWidget. Então calma, se ele é um blocContainer vou colocar direto um blocContainer, ele já é um blocContainer para nós, é só importar o blocContainer aqui. E meu localizationContainer recebe no construtor dele um componente, certo? Um widget, então ele tem que receber um Child que vai ser um widget.

Vamos falar para ele que ele vai receber um widget, que é um Child. Só que lembra, quero que isso seja por nome, o padrão é que isso seja por nome. Se é por nome ele é como um argumento por nome e quero que seja obrigatório, então @required.

Missing override aqui, o que é o missing override? Um método build. Um método build que é quando vamos construir nosso localizationContainer. Agora, claro, nosso localizationContainer vamos fazer o que? Vamos prover, vamos devolver aqui um provedor que proveu um componente de língua, qual a língua atual. Na verdade, qual o locale atual.

Então o que vou querer é um BlocProvider, que vai fornecer para mim meu CurrentLocale, que no meu caso vou colocar como uma string, um cubit, esse meu cubit é um BlocProvider. Faltou o CurrentLocaleCubit. Vamos criar o CurrentLocaleCubit. Estou criando aqui tudo no mesmo, por enquanto estou criando tudo no mesmo.

Ele estende um cubit de string, e o valor padrão dele vai ser uma língua padrão, vou colocar uma língua padrão, não vou armazenar essa língua, você pode depois armazenar em outros lugares, etc. Coloquei aqui um pt-br, língua padrão. Se eu quisesse um en padrão, en padrão. Você escolhe a língua que você vai colocar.

Deixei aqui como en. Meu BlocProvider é de CurrentLocaleCubit. Temos a língua padrão. O que falta no BlocProvider é falar na hora de criar e qual o Child. Então, meu create recebe o contexto e devolve para nós nosso CurrentLocaleCubit. Nosso Child sabemos quem é. É o Child que você passou como argumento, é o this.child.

Aqui o que podemos fazer? Relembra que esse cara, podemos até criar o field, mas ele reclama do underline, não podemos, porque ele é nomeável, então não pode ser desta forma, teria que ser desta forma aqui. Mas e se eu colocar o final, posso colocar o final? O final beleza.

Então this.child, coloquei aqui, esse está errado, é só uma vírgula, eu acho que é isso. Coloquei um CurrentLocaleCubit aqui, tem um @override em algum lugar que está zoado, acho que aqui faltou o ponto e vírgula. Isso. Podemos dar um hot restart. A gente vai no aplicativo e vemos que o aplicativo está rodando normal, maravilha.

Agora o que queremos fazer é eu queria de alguma maneira usar esse CurrentLocaleCubit. Para isso vamos precisar de uma localização, certo? Então isso já vou passar para um localization dart. Vou criar um novo arquivo chamado localization.dart. Lembrando que é localização e internacionalização, estou colocando localization e internationalization aqui.

O que vou colocar? Esse cubit e o localizationContainer. Esses dois ficam aqui. Importo tudo que tenho que importar. Aqui importamos quem temos que importar. Deve estar tudo funcionando. O que temos que fazer agora? Lá na nossa localização temos que de alguma maneira disponibilizar as mensagens de internacionalização. Você quer em português, em inglês, o que você quer?

Lembrando, vou mudar aqui para pt-br, para colocarmos alguma outra língua, e vamos no dashboardContainer. Ele provê um NameCubit, que é o Guilherme, e tem aqui o código dele. Eu queria de alguma maneira, baseado no meu contexto pegar a parte de internacionalização. Quer dizer, com esse contexto quero pegar alguém que lida com a internacionalização dessa tela. Vou separar um componente de internacionalização por tela, por screen, por view, no meu caso aqui.

Como vou fazer isso? Vou criar um componente i18n que vai ser o cara que vou criar, meu DashboardViewi18n. E meu DashboardViewi18n de internationalization vai receber o contexto. O que ele faz então? Criamos a classe i18n, essa classe vai ter que ter um construtor, certo?

Aqui em cima passei o contexto, então crio o construtor com contexto, e na verdade o que isso vai fazer é chamar um super com contexto, porque a internacionalização vou colocar numa classe base, numa view básica de internacionalização. Uma Viewi18n de um componente.

O i18n de uma view, vai estar agrupado por views. Você pode ir com o componente i18n, o que fizer sentido para você. Então, create class Viewi18n e aqui temos ela. Lembrando, se você vai fazer uma por view, maravilha, se você vai fazer uma por widget, maravilha, se você vai fazer uma por várias views, maravilha. Aquilo que fizer sentido a granularidade para você. Como para mim é por view estou mantendo assim, mas o mais genérico seria chamar componente.

Só que componente tem aquela palavra, o que é um componente, o que não é, etc. E aqui a abreviação, abrevio ou não abrevio, internationalization ou não internationalization? Vai de cada um, estou mantendo a abreviação, vai do padrão que você estiver seguindo.

Vamos colocar o construtor, está reclamando do construtor, mas azar, vou dar um copy paste nojento aqui. Vou colocar um this, vou fechar esse daqui, colocar assim. Por enquanto seria assim. Tenho minha view aqui, meu i18n. esse i18n meu final, maravilha.

E agora eu gostaria em alguns lugares de usar esse i18n. Por exemplo, transferência, essa mensagem transfer é i18n.transfer. Eu peço pelo transfer do i18n. Vamos criar um método transfer? Tenho que colocar. Por enquanto vou fazer da maneira tosca, hard coded a string, mas já melhoramos.

Aqui tenho transfer, aqui tenho i18n_transaction_feed. De novo, o padrão é importante, repare que não estou seguindo esse padrão, não estou usando camel case, estou usando o padrão com underline, peço perdão por estar quebrando o padrão da linguagem, eu acho que faz sentido aqui por causa da legibilidade que precisamos nessa situação, que é a mesma legibilidade que queremos ter aqui.

O próximo é change name, i18n_change_name. Peço ele, aqui está o return. Nós vamos melhorar isso tudo, não se preocupe, não se assuste. Estamos usando esse componente i18n, vamos ver se está funcionando. Está tudo certo. Podemos colocar um transfer só para testar.

Está funcionando. O que quero fazer agora? Quero mudar para que isso funcione de uma maneira um pouco diferente. Eu queria que isso de alguma maneira pegasse a minha língua. Lesse a minha língua. Isso é, o DashboardViewi18n acessasse o meu cubit, esse meu cubit de CurrentLocaleCubit.

Lembram como fazemos isso? Lá na nossa viewi18n, lembra que esse cara é genérico? Vou jogar no localization. Esse cara, ao invés de usar o contexto, ele recebe o build context, ao invés de fazer isso, ele vai fazer o seguinte, this., ele vai criar, ou vai disponibilizar para nós um locale, o cubit do locale.

Como fazemos para fazer isso mesmo? this.language = blocprovider.of<CurrentLocaleCubit>(context).state. Peguei o estado e criei a língua. Um final nesse cara, e ele reclama, porque estou criando aquele dentro, ele não vai deixar o final para nós, não dessa forma. Aqui tenho um ponto e vírgula a mais.

Quer dizer, é uma forma de eu ler, o problema dessa abordagem é o rebuild quando você troca a língua. Lembra? Nós precisamos de um bloc builder para perceber que algo foi trocado e reconstruir. E aí você vai tomar a decisão. O que você quer reconstruir quando é trocada a língua?

Fica aqui o que você quer reconstruir quando trocar o CurrentLocaleCubit. Em geral, muitos sistemas fazem, é trocar tudo. Em geral, é comum reinicializar o sistema. Quer dizer, ou voltar para a tela inicial, você faz um rebuild de tudo do começo. É comum fazer alguma coisa do gênero. Dá uma pensada como você vai querer fazer isso, da forma que estou fazendo simplesmente é um locale que você define e ele segue. E você pode definir como você quer fazer o build.

Já sabemos usar o bloc builder, você usa onde você quiser o bloc builder baseado nesse componente. Aqui temos ele, definimos o language. O que ele está reclamando agora? No dashboard.dart, importar esse cara. Maravilha. Temos esses campos. Se eu salvar, se eu rodar de novo está tudo funcionando.

Outro detalhe agora. Aqui temos, no viewi18n temos acesso a language. Ao super, seria o super viewi18n, eu teria que ter acesso a language, eu queria ter acesso a language. então o que eu queria fazer? Eu queria de alguma maneira verificar se a língua for pt-br devolve um valor, se não é devolve outro, e por aí vai.

Em outras palavras, eu ia querer ficar criando para cada um um mapa pt-br tal valor, en outro valor, pt-br um valor, en outro valor. Então melhor do que meu transfer devolver isso para mim, eu poderia fazer de outra maneira. O transfer, vou transformar ele em uma função que vai retornar localize, uma função chamada localize, no nosso i18n.

A função localize vai receber para nós o que? Um mapa, que é pt-br. Se é pt-br é transferir. Se é en, é transfer. Fechei. O que faltou foi criar o localize aqui. Vou criar com ajuda um método localize, recebe um mapa. Esses são os values. O que ele vai fazer? Ele vai retornar do values, ele vai procurar a chave que é a língua atual, é isso que ele vai fazer.

Como não tenho acesso, simplesmente jogo esse método aqui. Meu método localize está aqui, ele devolve desses valores o baseado nessa língua, e aqui temos o localize para chamar. Temos ele aqui, salvei, ele não está conseguindo chamar, porque o método lá está fechado, certo? Então o que vou querer fazer é que vou querer nesse método deixar ele aberto agora.

Deixei certo, localizo ele. Vamos só testar de novo, dar um rebuild. Tem que aparecer aqui transferir, pt-br. Da mesma maneira que tenho esse método aqui, o transfer, vou fazer aqui e aqui. Então aqui é change name, com aspas simples, transaction feed, e aqui é transações e mudar nome.

Retorna, rebuilda, e temos agora tudo em português. Vamos ver como ficaria em inglês? Legal, é só nosso cubit. Nosso cubit que temos que alterar. Vou alterar meu cubit para en. Repare, não estou dando suporte para uma língua não encontrada, transfer, transaction feed, change name.

Todo lugar que você precisar agora você pode usar um bloc builder, ou você pode herdar de um viewi18n. Você também pode criar aquela estrutura em cima do bloc builder do CurrentLocaleCubit. Dá para fazer uma estrutura do gênero também, da mesma maneira que estamos criando estruturas.

Dessa forma já fomos capazes de internacionalizar e suportar duas línguas, quando você vai querer mudar depende de você, criar uma feature, escolher a língua ou não, você carrega de uma posição da pessoa, você carrega da configuração da pessoa, de onde você quiser.

Uma pequena mudança que vou fazer aqui, ao invés de deixar o transfer assim, vou deixar um getter de um atributo que vai devolver literalmente o localize. Isso simplifica quando você tem só uma linha e você quer fazer algo equivalente a um atributo. Então, string get, todos eles estão devolvendo nomes, strings.

A implementação é de uma linha e tiro a chave final. Formato tudo. Errei alguma coisa, porque formatei. Return vai embora, formato tudo, arranco as linhas em branco que coloquei demais, e temos transfer, transaction feed e change name. Os parênteses vão embora, e aqui agora, sim, é como se fosse um atributo. Rebuildo e temos a nossa aplicação rodando. Paramos por aqui, daqui a pouco melhoramos tudo que fizemos.

Sobre o curso Flutter i18n: entenda abordagens de internacionalização

O curso Flutter i18n: entenda abordagens de internacionalização possui 132 minutos de vídeos, em um total de 32 atividades. Gostou? Conheça nossos outros cursos de Flutter em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Flutter acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas