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 geralmente implica adaptar o conteúdo para torná-lo relevante, acessível e relacionável para usuários em diferentes idiomas e regiões.

A localização não se resume apenas a traduzir palavras, mas sim a criar uma experiência que faça os usuários se sentirem em casa, independentemente do idioma. Por exemplo, plataformas globais como a Amazon tornam a troca de idiomas tão suave que parece quase mágica. Além de melhorar a experiência do usuário, esse recurso desempenha um papel crucial no impulsionamento dos negócios, alcançando um público mais amplo e promovendo conexões mais fortes com clientes em todo o mundo.

Conteúdo

O que é i18n e Por Que Usá-lo?

i18n, abreviação de internacionalização, significa que uma aplicação suporta múltiplos idiomas. “i18n” é derivado do fato de que há 18 letras entre o primeiro “i” e o último “n” em “internacionalização.” Trata-se de tornar seu aplicativo adaptável para públicos globais, lidando com tradução de textos, 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 às preferências e culturas dos usuários.

Vamos mergulhar de cabeça

Vamos criar uma aplicação web multilíngue muito simples com um recurso de alternância para o 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 – Conhecer o básico da internacionalização (i18n) e por que é importante irá fornecer contexto para o projeto. As seções anteriores deste artigo cobrem o essencial.

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

  4. Node.js – Certifique-se de que o Node.js está instalado no seu sistema para lidar com dependências. Você pode baixar a versão mais recente em Node.js.

  5. Gerenciador de Pacotes – É necessário o npm (incluído no Node.js) ou o 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 uma 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:

Instale as 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 o 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 a ser usado 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 habilitação da funcionalidade de tradução. Este arquivo é responsável por configurar o núcleo do processo de tradução e garantir que o recurso 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 os 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. Observe 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 alternar os idiomas 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 a partir do idioma detectado ou idioma padrão do i18next
  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 definido por padrão.

  • O Hook useTranslation expõe a instância 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 inclusividade 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 o layout do texto sejam invertidos para manter a legibilidade e o 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 o modo 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(() => {
    // Verificar 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 Hook useTranslation do react-i18next expõe a função t, que é usada para buscar texto traduzido.

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

Ao seguir esses passos, seu aplicativo deve estar totalmente funcional com as traduções integradas de forma transparente. Veja como ficou o resultado final de nosso aplicativo:

Confira a demo ao vivo e o código-fonte no GitHub

Conclusão

Criar sites que dão aos usuários a flexibilidade de 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.

Ao combinar a internacionalização (i18n) com ferramentas como React-i18next e estilização com Tailwind CSS, você pode construir aplicativos que são flexíveis, amigáveis e acessíveis a um público global.

Neste projeto, percorremos a configuração do i18n, adicionamos um seletor de idioma e incluímos um “modo escuro” para melhor usabilidade.

Referências

https://react.i18next.com/

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