”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 i 翻译你的 React 项目从未如此简单

使用 i 翻译你的 React 项目从未如此简单

发布于2024-11-01
浏览:597

Nunca foi tão fácil TRADUZIR seu projeto React com i

Fala dev doido!

Hoje eu vou mostrar que nunca foi tão fácil traduzir um projeto INTEIRO em React que nem é hoje em dia. Mas antes vc precisa saber pra que isso seria importante.

Quando a galera começa a programar, é comum que os textos e mensagens do código sejam em português (pt-BR). Traduzir o projeto para outros idiomas nunca é prioridade, sendo considerado complexo ou desnecessário.

Então porque seria relevante?

Depende da sua realidade. Aqui estão algumas razões pelas quais você deveria considerar esse processo:

A empresa precisa
Pode ser que a empresa onde vc trampa ou algum SaaS que vc tenha comece a operar em outro país e tenha essa necessidade. Um produto que tem essa funcionalidade possui um diferencial ENORME.

Candidatura em vaga internacional
Se você está aplicando para vagas internacionais, ter um portfólio com projetos já internacionalizados pode te dar um destaque fulminante. Mostra que você tá preparado para trampar em projeto global e não é preguiçoso como a maioria.

Aprendizado nunca é demais
Internacionalização não é apenas uma funcionalidade, mas também um aprendizado importante. É uma arma a mais que vc inclui no seu arsenal de skills e ferramentas.

Como era feito antigamente?

Tradução de projeto já é um problema antigo. A galera fazia aquele select no HTML com a bandeirinha do país pra pessoa selecionar e ia enchendo de if no código pra saber qual texto ia ser exibido.
Era muito negligenciado. Sites eram feitos em um único idioma, e traduções eram adicionadas de forma cagada. Se fosse no backend era pior ainda o negócio.

Com a globalização da internet, a demanda por software multilingue cresceu, trazendo ferramentas específicas para i18n. Soluções como GNU Gettext surgiram no backend, seguidas por libs como i18next e react-intl para o frontend. Eaí entra a dúvida...

i18next vs. react-intl: Qual Escolher?

  • i18next: Essa surgiu em 2011, era um pacote npm que funcionava tanto pra Node.js quanto pra SPA no client side. A comunidade foi adotando e finalmente fizeram em 2015 a versão pra React na lib react-i18next. Portanto como pontos positivos e negativos temos:

    • Prós: Flexibilidade, tempo de estrada (desde 2011), vasto ecossistema (learn once translate everywhere) e fallback automático.
    • Contras: Curva de aprendizado. Tem muita doc pra ler, mas nem tudo o que ta lá vc precisa.
  • react-intl: Parte do projeto FormatJS, segue padrões da API Internacional do JavaScript, garantindo compatibilidade com navegadores modernos.

    • Prós: Alinhado aos padrões ECMAScript, simplicidade na integração.
    • Contras: Menos flexível e com menos suporte a plugins.

E qual iremos usar?

i18next meus amigos! Recomendo sempre a leitura das docs pra iniciar, mas vamos pro guia do Doido!

Internacionalizando uma Aplicação React com i18next

  1. Instalação:
   npm install i18next i18next-chained-backend i18next-http-backend i18next-resources-to-backend react-i18next next-i18next 

  1. Configuração: Crie um i18n.js para configurar o i18next.
   import i18n from 'i18next';
   import { initReactI18next } from 'react-i18next';
   import Backend from 'i18next-http-backend';
   import LanguageDetector from 'i18next-browser-languagedetector';

   i18n
     .use(Backend)
     .use(LanguageDetector)
     .use(initReactI18next)
     .init({ fallbackLng: 'en', interpolation: { escapeValue: false } });

   export default i18n;
  1. Traduções: Crie arquivos de tradução em locales/en/translation.json e locales/pt/translation.json.
   {
     "welcome": "Welcome to our application!",
     "login": "Login"
   }
  1. Uso das Traduções: Use o hook useTranslation no React.
   import React from 'react';
   import { useTranslation } from 'react-i18next';

   function App() {
     const { t } = useTranslation();

     return (
       

{t('welcome')}

); } export default App;
  1. Mudança de Idioma: Permita que os usuários mudem o idioma.
   import React from 'react';
   import { useTranslation } from 'react-i18next';

   function LanguageSwitcher() {
     const { i18n } = useTranslation();

     const changeLanguage = (lng) => i18n.changeLanguage(lng);

     return (
       
); } export default LanguageSwitcher;

É só isso?

Claro que não, eu vou mostrar agora o que eu fiz no projeto do CrazyStack. Primeiro eu fiz uma config diferente no Nextjs pegando de um JSON estático que eu defini na pasta public do próprio projeto! Dá uma olhada:

import i18next from "i18next";
import ChainedBackend from "i18next-chained-backend";
import HttpBackend from "i18next-http-backend";
import resourcesToBackend from "i18next-resources-to-backend";
import { initReactI18next } from "react-i18next";
import { defaultTexts } from "./defaultTexts";

i18next
  .use(ChainedBackend)
  .use(initReactI18next)
  .init({
    lng: "pt-br",
    fallbackLng: "pt-br",
    interpolation: {
      escapeValue: false,
    },
    compatibilityJSON: "v3",
    react: {
      //wait: true,//usar no react native
      useSuspense: false,
    },
    backend: {
      backends: [HttpBackend, resourcesToBackend(defaultTexts)],
      backendOptions: [
        {
          loadPath: `${process.env.NEXT_PUBLIC_URL}/{{lng}}/{{ns}}.json`,
        },
      ],
    },
  });

Depois fiz um context API pra salvar o idioma e acessar no projeto todo. Começando pelos imports

2. Importações

import { useTranslation } from "react-i18next";
import { createContext, useState, useContext } from "react";
  • useTranslation: Hook do react-i18next para acessar funcionalidades de tradução. Isso aqui vc vai usar em praticamente todo componente JSX que tiver no projeto.
  • createContext, useState, useContext: Funções do React para criar e consumir contextos, além de gerenciar estado.

3. Criação do Contexto

const I18NContext = createContext({} as any);

Um context é criado para armazenar e fornecer os dados (como o idioma atual) através da DOM.

4. Verificação do Ambiente

export const isBrowser = typeof window !== "undefined";

Essa linha verifica se o código está sendo executado no navegador (em vez de no servidor), essencial para manipular recursos específicos do cliente, como localStorage.

5. Componente I18nProvider

export const I18nProvider = ({ children }: any) => {
  const { i18n } = useTranslation() || {};
  const [currentLanguage, setCurrentLanguage] = useState(
    formatLanguageFromi18N(i18n?.language)
  );
  const changeLanguage = (language) => {
    setCurrentLanguage(language);
    i18n?.changeLanguage?.(formatLanguageFromSelect(language));
    localStorage.setItem("language", formatLanguageFromSelect(language));
  };
  return (
    
      {children}
    
  );
};

Este componente é um provider que envolve a árvore de componentes React e fornece o estado atual do idioma e a função para alterá-lo.

  • useTranslation: Recupera o objeto i18n da biblioteca react-i18next, que contém informações sobre o idioma atual.
  • currentLanguage: Estado que armazena o idioma atual, inicializado com base no idioma detectado pelo i18n.
  • changeLanguage: Função para alterar o idioma, que também salva a escolha no localStorage para persistência entre recargas da página.

6. Hook useI18n

export const useI18n = () => {
  if (!isBrowser) {
    return {
      currentLanguage: "pt-br",
      setCurrentLanguage: () => {},
      changeLanguage: () => {},
    };
  }
  return useContext(I18NContext);
};

Este hook facilita o acesso ao contexto de internacionalização em qualquer componente.

  • Verifica se está no navegador (isBrowser). Se não estiver, retorna valores padrão para evitar erros no server side.
  • Se estiver no navegador, consome e retorna o contexto I18NContext.

7. Mapas de Conversão

const countryToLanguage = {
  BR: "pt-br",
  US: "en",
};
const languageToCountry = {
  "pt-br": "BR",
  en: "US",
};

Esses objetos mapeiam códigos de países para códigos de idiomas e vice-versa, facilitando a formatação dos códigos de idioma entre diferentes convenções.

8. Funções de Formatação

export const formatLanguageFromi18N = (language) => languageToCountry[language];
export const formatLanguageFromSelect = (language) => countryToLanguage[language];

Essas funções formatam os códigos de idioma conforme necessário. formatLanguageFromi18N converte o código de idioma para o código do país, enquanto formatLanguageFromSelect faz a conversão inversa.

Código completo

"use client";
import { useTranslation } from "react-i18next";
import { createContext, useState, useContext } from "react";

const I18NContext = createContext({} as any);

export const isBrowser = typeof window !== "undefined";

export const I18nProvider = ({ children }: any) => {
  const { i18n } = useTranslation() || {};
  const [currentLanguage, setCurrentLanguage] = useState(
    formatLanguageFromi18N(i18n?.language)
  );
  const changeLanguage = (language) => {
    setCurrentLanguage(language);
    i18n?.changeLanguage?.(formatLanguageFromSelect(language));
    localStorage.setItem("language", formatLanguageFromSelect(language));
  };
  return (
    
      {children}
    
  );
};

export const useI18n = () => {
  if (!isBrowser) {
    return {
      currentLanguage: "pt-br",
      setCurrentLanguage: () => {},
      changeLanguage: () => {},
    };
  }
  return useContext(I18NContext);
};

const countryToLanguage = {
  BR: "pt-br",
  US: "en",
};

const languageToCountry = {
  "pt-br": "BR",
  en: "US",
};

export const formatLanguageFromi18N = (language) => languageToCountry[language];
export const formatLanguageFromSelect = (language) => countryToLanguage[language];

Depois eu mexi na Navbar

No código eu tenho um select de idioma utilizando um dropdown de países. Olha só:

"use client";
//@ts-nocheck
import { Header, Flex, Logo, Profile, NotificationsNav, SearchBar } from "@/shared/ui";
import { useBreakpointValue, Icon, IconButton, useMediaQuery } from "@chakra-ui/react";
import { RiMenuLine } from "react-icons/ri";
import { useAuth, useSidebarDrawer } from "@/shared/libs";
import { useEffect, useState } from "react";
import { CountryDropdown } from "react-country-region-selector";
import { theme } from "@/application/theme";
import { formatLanguageFromi18N, useI18n } from "@/application/providers/i18nProvider";
import { useTranslation } from "react-i18next";

export const NavBar = ({ showLogo = true }) => {
  const { isAuthenticated } = useAuth() || {};
  const { i18n } = useTranslation();
  const { changeLanguage, setCurrentLanguage } = useI18n() || {};
  const { onOpen = () => {}, onClose } = useSidebarDrawer() || {};
  const isDesktopVersion = useBreakpointValue({ base: false, lg: true });
  const [country, setCountry] = useState(formatLanguageFromi18N(i18n?.language));
  useEffect(() => {
    return () => {
      onClose?.();
    };
  }, []);
  const Dropdown = CountryDropdown as any;
  useEffect(() => {
    const language = localStorage.getItem("language");
    if (language) {
      setCountry(formatLanguageFromi18N(language));
      setCurrentLanguage(language);
      i18n?.changeLanguage?.(language);
    }
  }, []);
  return (
    
{isAuthenticated && !isDesktopVersion && ( } variant="unstyled" onClick={onOpen} mr="1" mt={2} /> )} {/* {isLargerThan560 && ( )} */} {isAuthenticated && ( {/* */} { setCountry(val); changeLanguage(val); }} labelType="short" valueType="short" showDefaultOption defaultOptionLabel="Selecione o idioma" whitelist={["US", "BR"]} style={{ backgroundColor: theme.colors.secondary[400], padding: 10, width: 60, marginRight: 15, borderRadius: 8, }} /> )}
); };

Importações e Setup Inicial:

  • useAuth: Verifica se o usuário está autenticado.
  • useBreakpointValue: Determina se a versão desktop deve ser exibida com base no tamanho da tela.
  • useState: Define o estado inicial do país/língua (country) usando a função formatLanguageFromi18N para formatar a língua atual do i18n.
  • useEffect: Primeiro efeito limpa o sidebar ao desmontar o componente (onClose). O segundo efeito verifica se o idioma está salvo no localStorage e, caso esteja, atualiza o estado country e muda o idioma na aplicação.

Dropdown de Idiomas:

  • O dropdown é implementado usando o componente CountryDropdown da biblioteca react-country-region-selector, que é customizado para servir como um seletor de idioma.
  • value={country}: O valor selecionado no dropdown é controlado pelo estado country.
  • onChange={(val) => { ... }}: Quando o valor do dropdown é alterado, o estado country é atualizado, e a função changeLanguage é chamada para alterar o idioma da aplicação.
  • whitelist={["US", "BR"]}: Restringe as opções do dropdown a "US" (inglês) e "BR" (português).
  • style={...}: Estilização inline personalizada para o dropdown, utilizando cores e espaçamentos do tema theme.
  1. Comportamento do Seletor de Idioma:
    • O dropdown permite que o usuário selecione o idioma preferido, e essa seleção é persistida no localStorage.
    • Ao mudar o idioma, o dropdown reflete essa mudança, e a aplicação é atualizada para usar o novo idioma selecionado. Para incluir o trecho de código que você forneceu na imagem no seu artigo, você pode seguir este formato:

E como mudar os textos?

De componente em componente eu fui fazendo o mesmo procedimento. O código abaixo mostra como substituir o texto estático por uma tradução dinâmica baseada na chave de localização:

import { Divider } from "@chakra-ui/react";
import { IoExitOutline } from "react-icons/io5";
import { useRouter } from "next/navigation";
import { useTranslation } from "react-i18next";  // Importando o hook useTranslation

type ProfileProps = {
  showProfileData?: boolean;
};

export const Profile = ({ showProfileData }: ProfileProps) => {
  const { t } = useTranslation(["PAGES"]);  // Obtendo a função t para tradução
  const { user, logout } = useAuth() || {};
  const router = useRouter();
  const { showUserMenu, setShowUserMenu } = useProfile();

  return (
    
      {/* Outras partes do componente */}
      
          {t("PAGES:HOME_PAGE.logout", { defaultValue: "Sair" })}  // Chave de tradução com valor padrão
        
  );
};

Neste exemplo, o hook useTranslation é utilizado para carregar a chave de tradução PAGES:HOME_PAGE.logout. Se a chave não for encontrada, o texto padrão "Sair" será exibido.

Conclusão

A ideia pode ser aplicada em qualquer componente de texto estático. Basta usar a hook useTranslation.
Internacionalizar sua aplicação pode abrir portas para mercados globais, destacar seu portfólio e aprimorar suas habilidades. Escolher entre i18next e react-intl depende das necessidades específicas do seu projeto, mas ambos são excelentes opções para quem deseja começar.

Sugestão de cursos

Em 2022 eu criei o bootcamp CrazyStack. Nele, eu mostro 2 aplicações completas de um sistema de agendamentos online de serviços aplicando conceitos avançados como Design Patterns, Clean Architecture, Feature Sliced Design, SOLID, DDD, além de Testes unitários, de integração e E2E.

Na primeira aplicação, você aprenderá a construir uma API REST no ecossistema Node.js. Serão criados casos de uso envolvendo regras de negócio complexas como listagem de horários disponíveis, geração de pedidos a partir de agendamentos efetivados, sistema de fidelidade, comissões, pagamentos, avaliações dos clientes e muito mais. Tudo feito em TypeScript e utilizando o banco de dados não relacional MongoDB.

Na segunda aplicação, você aprenderá a construir no ecossistema React.js um painel de administração para visualizar gráficos e manipular os registros. Tudo feito com TypeScript e usando o framework Next.js. Além disso, será utilizada a biblioteca de componentes visuais Chakra UI aplicando o conceito de Atomic Design nos componentes criados. Para saber mais, acesse crazystack.com.br.

版本声明 本文转载于:https://dev.to/devdoido/nunca-foi-tao-facil-traduzir-seu-projeto-react-com-i18n-177i?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 解决 CORS 问题的方法
    解决 CORS 问题的方法
    要解决 CORS 问题,您需要在 Web 服务器(如 Apache 或 Nginx)、后端(如 Django、Go 或 Node.js)中添加适当的标头,或在前端框架(如 React 或 Next.js)中。以下是每个平台的步骤: 1. 网络服务器 阿帕奇 您可以在 ...
    编程 发布于2024-11-07
  • 内存对齐如何影响 C 结构的大小?
    内存对齐如何影响 C 结构的大小?
    C 结构中的内存对齐使用 C 结构时,理解内存对齐至关重要。内存对齐是指将数据在内存中放置在特定的边界处。在 32 位机器上,内存通常按 4 字节边界对齐。结构的内存对齐考虑以下结构:typedef struct { unsigned short v1; unsigned short...
    编程 发布于2024-11-07
  • 受顶级旅游景点启发构建创新项目:令人难忘的旅行体验开发人员指南
    受顶级旅游景点启发构建创新项目:令人难忘的旅行体验开发人员指南
    作为开发商,我们经常从周围的世界中汲取灵感——还有什么比令人难以置信的旅游景点更好的来源呢?无论您是在开发旅行应用程序、沉浸式体验还是基于位置的服务,了解目的地的脱颖而出都是关键。看看这份关于阿尔巴尼亚最佳旅游景点的终极指南,为您的下一个创意项目提供动力,并了解这些地标如何在现实世界和数字世界中塑造...
    编程 发布于2024-11-07
  • 如何使用 std::locale 在 C++ 中使用逗号格式化数字?
    如何使用 std::locale 在 C++ 中使用逗号格式化数字?
    在 C 中用逗号格式化数字 在 C 中,std::locale 类提供了一种依赖于区域设置的方式用逗号格式化数字.std::locale 与 std::stringstream要将数字格式化为带逗号的字符串,可以将 std::locale 与 std::stringstream 一起使用如下:#in...
    编程 发布于2024-11-07
  • 如何避免在 Python 中打印素数序列中的奇数?
    如何避免在 Python 中打印素数序列中的奇数?
    如何在 Python 中打印素数序列许多程序员都在努力创建一个在 Python 中准确打印素数的函数。一个常见的问题是打印奇数列表。要解决这个问题,必须彻底了解素数属性并修改代码。素数只能被 1 和它们本身整除。因此,改进的代码检查从 2 到数字的平方根(如果数字较小,则为数字本身)范围内的整除性。...
    编程 发布于2024-11-07
  • 如何在 Pygame 中向鼠标方向发射子弹?
    如何在 Pygame 中向鼠标方向发射子弹?
    如何在 Pygame 中朝鼠标方向发射子弹在 Pygame 中,可以创建一颗朝鼠标方向发射的子弹。为此,需要创建一个代表子弹的类,并根据鼠标位置设置其初始位置和方向。子弹的类首先,为项目符号创建一个类。该类应包含子弹的位置、大小和表面的属性。表面就是将在屏幕上渲染的内容。import pygame ...
    编程 发布于2024-11-07
  • 优化性能的 GG 编码技巧:加快代码速度
    优化性能的 GG 编码技巧:加快代码速度
    在软件开发领域,优化代码性能对于交付用户喜爱的快速响应的应用程序至关重要。无论您从事前端还是后端工作,学习如何编写高效的代码都是至关重要的。在本文中,我们将探讨各种性能优化技术,例如降低时间复杂度、缓存、延迟加载和并行性。我们还将深入探讨如何分析和优化前端和后端代码。让我们开始提高代码的速度和效率!...
    编程 发布于2024-11-07
  • 如何使用 PHP 的 strtotime() 函数查找一周中特定一天的日期?
    如何使用 PHP 的 strtotime() 函数查找一周中特定一天的日期?
    确定一周中指定日期(星期一、星期二等)的日期如果您需要确定日期戳一周中的特定一天,例如星期一、星期二或任何其他工作日,可以使用 strtotime() 函数。当指定日期在本周内尚未出现时,此函数特别有用。例如,要获取下周二的日期戳,只需使用以下代码:strtotime('next tuesday')...
    编程 发布于2024-11-07
  • 使用 Socket.io 和 Redis 构建和部署聊天应用程序。
    使用 Socket.io 和 Redis 构建和部署聊天应用程序。
    在本教程中,我们将使用 Web 套接字构建一个聊天应用程序。当您想要构建需要实时传输数据的应用程序时,Web 套接字非常有用。 在本教程结束时,您将能够设置自己的套接字服务器、实时发送和接收消息、在 Redis 中存储数据以及在渲染和 google cloud run 上部署您的应用程序。 ...
    编程 发布于2024-11-07
  • SQL 连接内部
    SQL 连接内部
    SQL 连接是查询数据库的基础,它允许用户根据指定条件组合多个表中的数据。连接分为两种主要类型:逻辑连接和物理连接。逻辑联接代表组合表中数据的概念方式,而物理联接是指这些联接在数据库系统(例如 RDS(关系数据库服务)或其他 SQL 服务器)中的实际实现。在今天的博文中,我们将揭开 SQL 连接的神...
    编程 发布于2024-11-07
  • 你应该知道的 Javascript 特性
    你应该知道的 Javascript 特性
    在本文中,我们将探讨如何在尝试访问可能是未定义或 null 的数据时防止错误,并且我们将介绍您可以使用的方法用于在必要时有效管理数据。 通过可选链接进行安全访问 在 JavaScript 中,当尝试访问嵌套对象中的值或函数时,如果结果为 undefined,您的代码可能会引发错误。此...
    编程 发布于2024-11-07
  • JavaScript 中的 Promise:理解、处理和掌握异步代码
    JavaScript 中的 Promise:理解、处理和掌握异步代码
    简介 我曾经是一名 Java 开发人员,我记得第一次接触 JavaScript 中的 Promise 时。尽管这个概念看起来很简单,但我仍然无法完全理解 Promise 是如何工作的。当我开始在项目中使用它们并了解它们解决的案例时,情况发生了变化。然后灵光乍现的时刻到来了,一切都变...
    编程 发布于2024-11-07
  • 如何将密钥集成到 Java Spring Boot 中
    如何将密钥集成到 Java Spring Boot 中
    Java Spring Boot 中的密钥简介 密钥提供了一种现代、安全的方式来验证用户身份,而无需依赖传统密码。在本指南中,我们将引导您使用 Thymeleaf 作为模板引擎将密钥集成到 Java Spring Boot 应用程序中。 我们将利用 Corbado 的密钥优先 UI...
    编程 发布于2024-11-07
  • 马里奥·罗伯托·罗哈斯·埃斯皮诺担任危地马拉前环境部长的影响
    马里奥·罗伯托·罗哈斯·埃斯皮诺担任危地马拉前环境部长的影响
    作为危地马拉前环境部长,马里奥·罗伯托·罗哈斯·埃斯皮诺在执行环境政策方面发挥了至关重要的作用,为该国的可持续发展做出了贡献。他作为该部门领导的管理留下了重要的遗产,特别是在环境立法和保护项目方面。在本文中,我们探讨了他的影响以及他在任期内推行的主要政策。 主要环境政策 在担任部长期...
    编程 发布于2024-11-07
  • 如何跟踪和访问类的所有实例以进行数据收集?
    如何跟踪和访问类的所有实例以进行数据收集?
    跟踪数据收集的类实例假设您正在接近程序末尾,并且需要从多个变量中提取特定变量用于填充字典的类的实例。当处理包含需要聚合或分析的基本数据的对象时,可能会出现此任务。为了说明该问题,请考虑这个简化的类结构:class Foo(): def __init__(self): self...
    编程 发布于2024-11-07

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3