Mudança de idioma com Getx
Fala pessoal!
Vamos para mais um artigo de Getx, e agora vamos dar uma avançada nas funcionalidades que estão lá para nos ajudar a desenvolver um aplicativo cada vez melhor!
É fato que nem sempre seu aplicativo vai precisar ser traduzido para vários idiomas, porém nós sempre devemos pensar que nosso projeto vai crescer, ter demanda e principalmente ser necessário ser traduzido para vários idiomas.
O QUE VAMOS FAZER AQUI
Nesse artigo vamos pegar o nosso último projeto do TODO, que está todo em inglês e colocar inicialmente para: inglês, português e espanhol, persistindo no GetStorage, que é mais uma ferramenta do mesmo segmento no getx que substitui o SharedPreference, o último idioma selecionado
GitHub – brasizza/hive-medium-p3
Repositório acima será o que vamos atualizar para trabalhar com essas funcionalidades descritas acima!
PREPARANDO O PROJETO
Como foi explicado nos artigos anteriores, esse projeto está bem simples, sem repositories, services entre outros, pois é somente para demonstrar de um jeito simples as funcionalidades.
Vamos incluir no nosso projeto o package do get_storage
get_storage: ^2.0.3
Estou deixando aqui as bandeiras que estou utilizando no sistema para que possa ser traduzido pra qualquer idioma abaixo pra ficar todas as bandeiras padronizadas!
Com as bandeiras em mãos vamos criar nossa estrutura de pastas na raiz do projeto. Vamos criar uma pasta assets, e dentro delas uma pasta flags onde vamos colocar todas essas bandeiras e uma pasta chamada locales, que vamos passar por ela no decorrer do artigo
Como transformar um aplicativo de uma língua, para um de várias
Em muitos casos o app já existe e não existia a demanda de várias línguas. Então para começar iremos transformar todos os nossos textos em códigos, para que possam ser traduzidos para qualquer língua que adicionarmos no sistema.
Se abrirmos o arquivo home_view.dart, iremos ver que o título da AppBar e o texto quando não existe tarefas está em inglês, porém vamos começar a traduzir nosso app para português.
O primeiro passo é padronizar as tags de tradução para não existir nenhuma ambiguidade e tentar reutilizar o máximo de palavras genéricas possíveis para facilitar a tradução. Pensando nisso nós iremos fazer a seguinte convenção: Nossas tags serão sempre iniciadas com nome do modulo.campo, ou seja, no exemplo do app bar nós iremos colocar home.title_page e assim por diante , ficando parecido com o json abaixo que deverá ser criado lá naquela pasta de locales.
wcriamos labels genéricas do tipo save, delete, yes e no para poder ser reutilizado em qualquer parte do nosso projeto e assim evitando várias palavras iguais.
Basicamente nossos texts agora ficarão assim. sempre lembrando que no caso do .tr vc necessariamente tem que remover o const, caso tenha colocado, pois o text não será mais uma constante pois o valor mudará
Text('home.title_page'.tr)
Um ponto interessante é na linha 14 do json, onde colocamos uma variável que vai receber alguma informação dentro da tradução.
vamos em dialog_todo_widget.dart, na linha 108, vemos que o nosso dialog se encontra assim:
Get.defaultDialog(title: 'Delete task #${todo!.id}', content: const Text('Are you sure you want to delete this task?'), actions: [
o que vamos fazer é alterar a tradução para que o todo!.id seja parte da tradução sem que a gente precise fazer concatenação de strings ou algo do tipo, ficando assim:
Get.defaultDialog(title: 'dialog.delete_task.title'.trParams({'task_id': todo!.id.toString()}), content: Text('dialog.delete_task.description'.tr), actions: [
o .trParams faz com que você consiga passar N atributos para a sua tradução para que possa ser incluída dentro do texto, facilitando e muito o processo de tradução!
COMO INCLUIR ISSO NO NOSSO PROJETO
Com todos os textos mapeados e salvo na pasta locales, nós já temos nossa primeira língua no nosso sistema. porém nós temos que criar a classe de tradução e também indicar ao Getx que ele vai utilizar nossa classe de tradução e claro definir a língua inicial do app
CRIANDO A CLASSE DE TRADUÇÃO
O Getx já tem uma classe abstrata de tradução Translations. O que vamos fazer é criar nosso sistema de tradução extendendo essa classe.
Se você olhar na documentação, o método de criação da tradução é meio manual e você tem que ficar jogando o map direto na classe de tradução
(vide documentação)
O que vamos fazer aqui é um método diferente onde ele lê do arquivo json e gera essa classe de Tradução, já que a tradução não precisa ser dinâmica, pois você precisa criar o arquivo, traduzir e colocar no sistema para produção, talvez em um próximo artigo a gente faça uma api para pegar as traduções em tempo real.
Nesse código acima estamos criando uma classe AppTranslation e quando executamos o init(), lemos o nosso arquivo json traduzido do pt.json e jogamos na chave ‘pt’ que é implementada la no Translations, assim estamos utilizando o método de tradução do Getx sem alterar absolutamente nada.
INDICANDO NO MAIN NOSSA TRADUÇÃO
Precisamos fazer 2 alterações Básicas no nosso main que é indicar nossa classe de tradução e indicar uma língua inicial e uma língua para o caso de não existir uma tradução pronta, lembrando que o seu main precisa ser async e colocar o WidgetsFlutterBinding.ensureInitialized(); na primeira linha do main para que esse tipo de carregamento possa ser feito!
GetMaterialApp( title: "Application", initialRoute: AppPages.INITIAL, getPages: AppPages.routes, translations: await AppTranslation().init(), locale: Get.deviceLocale, fallbackLocale: const Locale('pt', 'BR'), )
Fazendo isso nós indicamos no locale a língua do device , e um fallbackLocale que é pra onde ele vai caso a tradução não exista para a língua especificada, e além disso nossa classe de tradução para ele juntar tudo isso em um sistema único de tradução.
CRIANDO UM MODELO DE LÍNGUAS
Para facilitar nosso trabalho, vamos criar um modelo de língua, para podermos colocar um listview de seleção das línguas disponíveis
Feito isso vamos criar nossa LanguageController e nossa LanguagePage que respectivamente vai ter nosso método para pegar as línguas disponíveis em uma propriedade observável e jogar para dentro da nossa lista para fazer uma lista horizontal de todas as línguas disponíveis no nosso sistema, com um evento de clique na bandeira para mudar a língua.
No código acima nós fixamos somente uma bandeira incialmente no nosso sistema, pois nós somente temos a tradução para o português.
No código acima criamos a lista na horizontal baseado em uma lista de línguas que vem do nosso controller que por sua vez é colocado no home_view, que vamos colocar dentro do appbar, mas antes é necessário colocar o nosso LanguageController no binding do home, para que ele possa ser injetado e a lista ser carregada no momento da inicialização da tela
Na nossa home_view, iremos na nossa AppBar, criar no bottom dela a listagem propriamente dita, porém inicialmente irá aparecer somente a bandeira do brasil
appBar: AppBar( title: Text('home.title_page'.tr), centerTitle: true, bottom: const PreferredSize(child: LanguagePage(), preferredSize: Size.fromHeight(50)), ),
Se você fez tudo certo ate aqui, trocando os textos pelos codigos de tradução, sua tela estará basicamente igual essa:
FACILIDADES USANDO GETX
Agora que vem a grande sacada do Getx, vamos criar mais uma língua para testes na pasta locales, incluir nos textos traduzidos e vamos chamar de en.json
e incluindo no nosso language_controller mais uma linha de línguas
languages.add(Language(lang: 'en', contryCode: 'US', flag: 'usa.png'));
e só de executar o restar da sua aplicação ele já vai criar mais um botão de língua, agora com a bandeira dos Estados Unidos
COMO MUDO A LÍNGUA
A mudança de língua é uma das coisas mais fáceis do Getx, e para isso vamos alterar lá no nosso LanguagePage, deixamos um GestureDetector preparado já deixando um objeto _lang pronto para ser utilizado, nesse caso utilizaremos o método Get.updateLocale que basicamente espera um Locale que pedem exatamente os campos que temos no nosso objeto, a língua e opcionalmente o país, caso tenha mais de uma tradução pra mesma língua, no caso do espanhol.
Fazendo isso, seu sistema já está alternando entre as línguas clicadas de um jeito extremamente simples e funcional!
onTap: () async { await Get.updateLocale(Locale(_lang.lang, _lang.contryCode)); },
PERSISTINDO A ESCOLHA DO IDIOMA
Agora vem a parte interessante. Se você escolher a língua inglesa e atualizar seu aplicativo, vai ver que ele volta para o português, pois no nosso main está informando que a nossa língua inicial é a que está no aparelho, que no nosso caso é português. E como fazemos para persistir essa informação? E a resposta é simples GetStorage. O GetStorage é um método de persistência de dados de chave/valor igual o sharedPrefrences, porém já que estamos utilizando todo o ambiente do Getx vamos utilizá-lo também, especialmente pelo método de listen da chave quando muda seu valor para a troca do nosso idioma.
INICIANDO O GETSTORAGE
Vamos criar um método no nosso main para criar a instância do GetStorage e injetar dentro do nosso ambiente do Getx com o put sendo permanente para usar em qualquer lugar
_initGetStorage() async { await GetStorage.init('preferences'); final storage = GetStorage('preferences'); Get.put<GetStorage>(storage, tag: 'preferences', permanent: true); }
e incluímos esse método entro do nosso main , logo abaixo ao _initHive, assim nós iremos iniciar o GetStorage logo na subida inicial da nossa aplicação.
Então vamos na nossa home_controller, e vamos incluir no onReady, as informações de escuta do GetStorage, pois o onReady é chamado quando o último frame da tela é carregado e como queremos alterar o idioma depois que ele carrega toda a tela, é ai que devemos colocar!
@override void onReady() async { super.onReady(); final storage = Get.find<GetStorage>(tag: 'preferences'); final readLanguage = storage.read('language'); if (readLanguage != null) { final _lang = Language.fromJson(readLanguage); await Get.updateLocale(Locale(_lang.lang, _lang.contryCode)); } storage.listenKey('language', (_lang) async { await Get.updateLocale(Locale(_lang.lang, _lang.contryCode)); }); }
O que estamos fazendo ai é pegando a instancia do GetStorage e vendo se existe alguma coisa para a chave language, caso exista nós pegamos essa língua que está persistida na chave e trocamos para a língua selecionada e então colocamos uma escuta na chave ‘language’ para que quando ela for alterada eu pegue a língua selecionada e altere.
Porém agora precisamos retirar a troca de língua no nosso onTap ali do LanguagePage e colocar a instrução de salvar a língua no GetStorage, que por sua vez, por estar escutando a chave ‘language’ , vai automaticamente alterar para a língua selecionada, ficando bem simples assim
onTap: () async { await Get.find<GetStorage>(tag: 'preferences').write('language', _lang); },
O que estamos fazendo é basicamente colocando o valor na chave , que vai chamar o listen la do homeController que foi iniciado no onReady que vai alterar automaticamente. Ao reiniciar a aplicação ele vai entrar no mesmo onReady , porém vai existir dados na chave e ai ele também vai alterar a língua para a última língua selecionada!
E é isso pessoal!
Agora o aplicativo está em 2 línguas, podendo ser adicionadas outras dezenas dependendo somente dos arquivos jsons de tradução!
Segue o git do projeto, resolvi deixar pro final para vocês tentarem fazer lendo o artigo!