Recentemente, trabalhei em um projeto empolgante que envolveu a criação de um site capaz de alternar entre idiomas para atrair um público mais amplo. Isso me fez entender melhor o conceito de “localização”, que normalmente envolve adaptar o conteúdo para torná-lo relevante, acessível e identificável para usuários em diferentes idiomas e regiões.

Localização não se trata apenas de traduzir palavras, mas de criar uma experiência que faça os usuários se sentirem em casa, independentemente do seu idioma. Por exemplo, plataformas globais como a Amazon tornam a troca de idioma tão fluida que parece quase mágica. Além de melhorar a experiência do usuário, esse recurso desempenha um papel crucial em impulsionar negócios ao alcançar um público mais amplo e fomentar conexões mais fortes com clientes em todo o mundo.

Índice

O que é i18n e por que usá-lo?

i18n, abreviação de internacionalização, significa que um aplicativo suporta múltiplas línguas. “i18n” é derivado do fato de que há 18 letras entre o primeiro “i” e o último “n” em “internacionalização.” É tudo sobre tornar seu aplicativo adaptável para audiências globais, lidando com tradução de texto, formatando datas e números, gerenciando moedas e acomodando convenções regionais.

Ao habilitar a internacionalização, seu aplicativo se torna não apenas uma ferramenta, mas uma plataforma inclusiva que fala diretamente à preferência e cultura do usuário.

Vamos direto ao ponto

Vamos criar uma aplicação web multilíngue muito simples com um recurso de alternância de modo escuro para demonstrar como alcançar esse conceito.

Pré-requisitos

  1. Conhecimento Básico de React – Você deve entender como criar componentes, gerenciar estado e usar Hooks como useState e useEffect. Se você é novo no React, recomendo começar com a documentação oficial do React para uma base sólida.

  2. Familiaridade com Conceitos de Internacionalização – Saber o básico da internacionalização (i18n) e por que é importante fornecerá contexto para o projeto. As seções anteriores deste artigo cobrem os fundamentos.

  3. Tailwind CSS – Vamos usar o Tailwind CSS para estilização. É um framework de CSS de primeira utilidade que ajuda a construir designs modernos e responsivos sem sair do seu HTML. Se você não conhece, confira a documentação do Tailwind.

  4. Node.js – Certifique-se de que o Node.js esteja instalado em seu sistema para lidar com dependências. Você pode baixar a última versão em Node.js.

  5. Gerenciador de Pacotes – É necessário ter npm (incluído com o Node.js) ou yarn para gerenciar as dependências do projeto

Ferramentas que vamos usar

  1. Editor de Código

  2. Biblioteca de Localização: react-i18next

  3. Biblioteca de Ícones: hero-icons

Passo 1: Como Configurar o Projeto

Inicialize o Projeto

Use o Vite para configuração rápida:

npm create vite@latest multilingual-demo

Siga as instruções que aparecem no seu terminal, selecionando React e TypeScript para desenvolvimento conforme mostrado na imagem abaixo:

Instalar Dependências

Execute os seguintes comandos no seu terminal para instalar as dependências necessárias para este projeto:

npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backend heroicons 
npm install tailwindcss postcss autoprefixer  
npx tailwindcss init

Configure o TailwindCSS

Atualize o arquivo tailwind.config.ts:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  darkMode: "class", //Para a nossa funcionalidade de modo escuro
  theme: {
    container: {
      center: true,
      padding: "1.25rem",
      screens: {
        sm: "1200px",
      },
    },
    extend: {},
  },
  plugins: [],
};

Adicione TailwindCSS ao arquivo src/index.css:

@tailwind base;  
@tailwind components;  
@tailwind utilities;

Passo 2: Como Configurar a Internacionalização com i18next

Inicialize o i18next

Crie um arquivo i18n.tsx na pasta src e configure o i18next:

import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-http-backend";

i18next.use(LanguageDetector).use(initReactI18next).use(Backend).init({
  returnObjects: true,
  fallbackLng: "en", // Idioma para retornar se o selecionado não estiver configurado
  debug: true, // Para nos permitir ver erros
  //   lng: "en", // Idioma padrão como inglês
});

Vamos dar uma olhada rápida no conteúdo deste arquivo, pois ele desempenha um papel fundamental na ativação da funcionalidade de tradução. Este arquivo é responsável por configurar o núcleo do processo de tradução e garantir que a funcionalidade de troca de idioma funcione sem problemas em seu aplicativo.

  • i18next: A biblioteca interna de internacionalização que estamos usando para tradução.

  • LanguageDetector: Nos ajuda a detectar automaticamente o idioma preferido do usuário, com base nas configurações do navegador.

  • initReactI18next: É responsável por integrar o plugin i18next com o React e fornece Hooks como o Hook useTranslation e outros utilitários.

  • Backend: Busca dinamicamente dados de tradução de uma fonte externa. Neste caso, estaremos usando arquivos JSON.

Importe este arquivo no arquivo main.tsx:

//main.tsx

import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import "./i18n.tsx";  //Importe aqui

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <React.Suspense fallback="loading">
      <App />
    </React.Suspense>
  </StrictMode>
);

Criar Arquivos de Tradução

No diretório public/locales, crie subpastas para cada idioma (por exemplo, en, fr) e inclua arquivos translation.json:

en/translation.json

{
    "greeting": "Welcome to the Language Playground",
    "detail": {
        "line1": "Did you know that over 7,000 languages are spoken worldwide?",
        "line2": "This Playground demonstrates how web applications can support users in multiple languages, making them accessible and inclusive to people from different backgrounds."
    }
}

fr/translation.json

{
    "greeting": "Bienvenue sur le terrain de jeu linguistique",
    "detail": {
        "line1": "Saviez-vous que plus de 7 000 langues sont parlées dans le monde ?",
        "line2": "Ce terrain de jeu démontre comment les applications web peuvent prendre en charge les utilisateurs dans plusieurs langues, les rendant accessibles et inclusives aux personnes de différents horizons."
    }
}

Aqui, você pode adicionar quantos idiomas desejar com seus arquivos de tradução que serão fornecidos ao i18next. Note que as chaves nos arquivos JSON são as mesmas que serão usadas como referências ao exibi-las no site.

Passo 3: Como Construir Componentes

Crie uma pasta components no diretório src e adicione os seguintes componentes:

Seletor de Idioma

Crie o componente LanguageSelector – contém um elemento select para ajudar os usuários a trocar de idioma dinamicamente:

import { useEffect, useState } from "react";
import i18next from "i18next";
import { useTranslation } from "react-i18next";

type languageOption = { language: string; code: string };

const languageOptions: languageOption[] = [
  {
    language: "English",
    code: "en",
  },
  { language: "French", code: "fr" },
  { language: "German", code: "de" },
  { language: "Spanish", code: "es" },
  { language: "Arabic", code: "ar" },
  { language: "Yoruba", code: "yo" },
];

const LanguageSelector = () => {
  // Defina o idioma inicial detectado pelo i18next ou o idioma padrão
  const [language, setLanguage] = useState(i18next.language);

  const { i18n } = useTranslation();

  const handleLanguageChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const selectedLanguage = e.target.value;
    setLanguage(selectedLanguage);
    i18next.changeLanguage(selectedLanguage); // Atualize o idioma no i18next
  };

  useEffect(() => {
    document.body.dir = i18n.dir(); // Define o corpo para ltr ou rtl
  }, [i18n, i18n.language]);

  return (
    <select
      id="language"
      value={language}
      onChange={handleLanguageChange}
      className="p-2 border border-gray-300 rounded-md shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
        dark:bg-gray-800 dark:border-gray-600 dark:text-gray-200 dark:focus:border-indigo-400 dark:focus:ring-indigo-700 dark:focus:ring-opacity-50"
    >
      {languageOptions.map(({ language, code }, key) => (
        <option value={code} key={key}>
          {language}
        </option>
      ))}
    </select>
  );
};

export default LanguageSelector;
  • Inicialize o idioma com o idioma detectado pelo i18next ou o idioma padrão definido.

  • O Hook useTranslation expõe a instância do i18n do i18next para interagir com as configurações de internacionalização.

  • A função handleLanguageChange seria usada para atualizar o idioma selecionado pelo usuário. É acionada quando o usuário seleciona um novo idioma no menu suspenso.

Implementando a Direção do Texto

O atributo dir em HTML é um recurso crítico para garantir acessibilidade e inclusão em aplicações web, especialmente ao lidar com idiomas que diferem na direção do texto. Por exemplo:

  • Da Esquerda para a Direita (LTR): A maioria dos idiomas, incluindo inglês, francês e espanhol, seguem essa direção.

    Da Direita para a Esquerda (RTL): Idiomas como árabe e hebraico exigem que o alinhamento e layout do texto sejam invertidos para manter a legibilidade e contexto cultural.

Para alcançar isso em nosso aplicativo, definimos o document.body.dir para o dir do i18n enquanto ouvimos as mudanças na seleção de idioma usando o gancho useEffect

Alternar Modo Escuro

Crie o componente DarkModeToggle para alternar entre os modos claro e escuro conforme preferido pelo usuário.

import { useEffect, useState } from "react";
import { SunIcon, MoonIcon } from "@heroicons/react/solid";

const DarkModeToggle = () => {
  const [darkMode, setDarkMode] = useState(false);

  useEffect(() => {
    // Verifique o armazenamento local ou preferência do sistema na primeira carga
    const isDark =
      localStorage.getItem("theme") === "dark" ||
      (!localStorage.getItem("theme") &&
        window.matchMedia("(prefers-color-scheme: dark)").matches);
    setDarkMode(isDark);
    document.documentElement.classList.toggle("dark", isDark);
  }, []);

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
    document.documentElement.classList.toggle("dark", !darkMode);
    localStorage.setItem("theme", !darkMode ? "dark" : "light");
  };

  return (
    <button
      aria-label="Toggle dark mode"
      onClick={toggleDarkMode}
      className="p-1 rounded"
    >
      {darkMode ? (
        <SunIcon
          className="w-6 h-6 text-yellow-500 "
          onClick={toggleDarkMode}
        />
      ) : (
        <MoonIcon className="w-6 h-6 text-gray-900 " onClick={toggleDarkMode} />
      )}
    </button>
  );
};

export default DarkModeToggle;

Componente de Cabeçalho

O componente Header serve como um componente pai para os componentes DarkModeToggle e languageSelector.

import DarkModeToggle from "./DarkModeToggle";
import LanguageSelector from "./LanguageSelector";

const Header = () => {
  return (
    <header className="container flex justify-between">
      <DarkModeToggle />
      <LanguageSelector />
    </header>
  );
};

export default Header;

Passo 4: Componente Principal do Aplicativo

No arquivo src/app, inclua o seguinte:

import { useTranslation } from "react-i18next";
import Header from "./components/Header";

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

  const line1 = t("detail.line1");
  const line2 = t("detail.line2");

  return (
    <div className="h-[100vh] bg-white text-black dark:bg-gray-900 dark:text-white py-8">
      <Header />
      <div className="container text-center max-w-2xl mt-28">
        <h1 className="text-4xl font-bold">{t("greeting")}</h1>
        <p className="mt-8">{line1}</p>
        <p className="mt-2">{line2}</p>
      </div>
    </div>
  );
};

export default App;
  • O useTranslation Hook do react-i18next expõe a função t, que é usada para buscar texto traduzido.

  • Ela busca a string traduzida com base em uma chave dos seus arquivos de tradução (por exemplo, en.json, fr.json).

Seguindo esses passos, seu aplicativo deve estar agora totalmente funcional com traduções integradas de forma fluida. É assim que o resultado final do nosso aplicativo se parece:

Confira a demonstracão ao vivo e o código fonte no GitHub

Conclusão

Criar sites que permitem aos usuários selecionar seu idioma preferido não é apenas uma conquista técnica, mas um passo em direção a tornar a web mais inclusiva e acolhedora.

Combinando internacionalização (i18n) com ferramentas como React-i18next e estilização com Tailwind CSS, você pode construir aplicações que são flexíveis, amigáveis ao usuário e acessíveis a uma audiência global.

Neste projeto, percorremos a configuração do i18n, adicionando um seletor de idiomas e incluindo o “modo escuro” para melhor usabilidade.

Referências

https://react.i18next.com/

https://www.youtube.com/watch?v=dltHi9GWMIo