Matheus Breguêz (matbrgz)
Boas Práticas de Internacionalização (i18n) em React e Next.js em 2025
Desenvolvimento

Boas Práticas de Internacionalização (i18n) em React e Next.js em 2025

Índice

Boas Práticas de Internacionalização (i18n) em React e Next.js em 2025

Em um mundo digital cada vez mais conectado, desenvolver aplicações web que atendam a usuários globais não é mais um diferencial, mas uma necessidade. A internacionalização (i18n) tornou-se um componente crítico no desenvolvimento de aplicações modernas, permitindo que seu produto alcance públicos diversos, independentemente de idioma, região ou preferências culturais.

Neste artigo, exploraremos as melhores práticas, ferramentas e estratégias para implementar internacionalização eficaz em aplicações React e Next.js em 2025, fornecendo exemplos práticos e soluções para desafios comuns.

Fundamentos da Internacionalização

O que é i18n e por que é importante?

O termo “i18n” é uma abreviação para “internacionalização” (a letra “i” seguida por 18 letras e terminando com “n”). É o processo de projetar e desenvolver aplicações de software que podem ser adaptadas para diferentes idiomas e regiões sem alterações de engenharia ou código.

Em 2025, a importância da i18n é amplificada por vários fatores:

  1. Alcance global: Aplicações com suporte multilíngue podem atingir mercados internacionais e expandir sua base de usuários.
  2. Experiência do usuário: Usuários se sentem mais confortáveis e engajados quando interagem com aplicações em seu idioma nativo.
  3. Conformidade regulatória: Muitos países têm requisitos legais para sistemas digitais oferecerem suporte a idiomas locais.
  4. Vantagem competitiva: Aplicações bem internacionalizadas se destacam em mercados globais competitivos.

Diferença entre i18n, l10n e g11n

É importante compreender a distinção entre termos frequentemente usados neste domínio:

  • Internacionalização (i18n): Processo de design e desenvolvimento de um produto para que possa ser adaptado a diferentes idiomas e regiões.
  • Localização (l10n): Processo de adaptar um produto internacionalizado para um local específico ou mercado, incluindo tradução de textos e adaptação de elementos culturais.
  • Globalização (g11n): Estratégia de negócios que abrange tanto i18n quanto l10n, considerando todos os aspectos de levar um produto a mercados globais.

Bibliotecas e Ferramentas Modernas para i18n em React

Ecossistema de i18n em 2025

O ecossistema de internacionalização para React evoluiu significativamente nos últimos anos. Aqui estão as bibliotecas mais populares e avançadas em 2025:

1. React-i18next

O react-i18next continua sendo uma das soluções mais robustas e populares, evoluindo para atender às necessidades modernas:

// Configuração básica do react-i18next em 2025
import i18n from 'i18next';
import { initReactI18next, useTranslation } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';

i18n
  // Carregamento sob demanda de traduções
  .use(Backend)
  // Detecção automática de idioma
  .use(LanguageDetector)
  // Integração com React
  .use(initReactI18next)
  .init({
    fallbackLng: 'pt-BR',
    supportedLngs: ['pt-BR', 'en-US', 'es', 'fr', 'zh-CN'],
    
    // Novo em 2025: Detecção avançada de idioma com preferências de usuário
    detection: {
      order: ['localStorage', 'navigator', 'querystring', 'cookie'],
      caches: ['localStorage'],
      cookieExpirationDate: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
      lookupQuerystring: 'lng',
      lookupCookie: 'i18n',
    },
    
    // Novo em 2025: Cache inteligente com estratégia adaptativa
    backend: {
      loadPath: '/locales//.json',
      requestOptions: {
        cache: 'smart-default',  // Nova estratégia de cache adaptativa
      },
    },
    
    interpolation: {
      escapeValue: false, // React já escapa por padrão
      format: function(value, format, lng) {
        // Novo suporte para formatação avançada
        if (format === 'uppercase') return value.toUpperCase();
        if (format === 'currency') return new Intl.NumberFormat(lng, { style: 'currency', currency: 'BRL' }).format(value);
        return value;
      }
    },
    
    // Novo em 2025: Análise de uso de traduções para otimização
    telemetry: {
      enabled: process.env.NODE_ENV === 'development',
      endpoint: '/api/i18n-telemetry',
      sampleRate: 0.1
    }
  });

export default i18n;

2. Formato ICU com react-intl

O formato ICU (International Components for Unicode) tornou-se o padrão predominante para manipulação de textos internacionalizados:

// Exemplo usando react-intl com sintaxe ICU moderna
import React from 'react';
import { FormattedMessage, useIntl } from 'react-intl';

function ProductDetails({ product, inventory, lastUpdated }) {
  const intl = useIntl();
  
  return (
    <div className="product-card">
      <h2>{product.name}</h2>
      
      {/* Pluralização avançada */}
      <FormattedMessage
        id="product.inventory"
        defaultMessage="{inventory, plural, =0 {Fora de estoque} one {Última unidade disponível!} other {# unidades em estoque}}"
        values=
      />
      
      {/* Formatação de data relativa */}
      <FormattedMessage
        id="product.lastUpdated"
        defaultMessage="Atualizado {lastUpdated, relativeTime, style=long}"
        values=
      />
      
      {/* Formatação de valores variáveis dependentes de idioma */}
      <p>
        {intl.formatMessage(
          {
            id: 'product.price',
            defaultMessage: 'Preço: {price, number, currency}'
          },
          { price: product.price }
        )}
      </p>
      
      {/* Formatação condicional com seleção */}
      <FormattedMessage
        id="product.status"
        defaultMessage="{status, select, new {Novo} sale {Promoção} limited {Edição limitada} other {Regular}}"
        values={{ status: product.status }}
      />
    </div>
  );
}

3. LinguiJS: Uma alternativa poderosa

LinguiJS ganhou popularidade significativa por sua simplicidade e desempenho:

// Exemplo com Lingui v5 (versão 2025)
import React from 'react';
import { Trans, Plural, t } from '@lingui/macro';

function ShoppingCart({ items, totalPrice, lastUpdated }) {
  return (
    <div className="shopping-cart">
      <h2><Trans id="cart.title">Seu Carrinho</Trans></h2>
      
      <Plural
        value={items.length}
        zero={<Trans id="cart.empty">Seu carrinho está vazio</Trans>}
        one={<Trans id="cart.oneItem">1 item no carrinho</Trans>}
        other={<Trans id="cart.items">{items.length} itens no carrinho</Trans>}
      />
      
      {items.map(item => (
        <div key={item.id} className="cart-item">
          <span>{item.name}</span>
          <span>{t({
            id: 'cart.item.price',
            message: 'Preço: {price, number, currency}',
            values: { price: item.price }
          })}</span>
        </div>
      ))}
      
      <div className="cart-footer">
        <div className="total">
          <Trans id="cart.total" values={{ total: totalPrice }}>
            Total: {totalPrice, number, currency}
          </Trans>
        </div>
        
        <div className="updated">
          <Trans id="cart.updated" values={{ date: lastUpdated }}>
            Atualizado em {lastUpdated, date, long}
          </Trans>
        </div>
      </div>
    </div>
  );
}

Implementação de i18n em Next.js

Next.js se consolidou como um dos frameworks React mais populares, e suas capacidades de internacionalização evoluíram consideravelmente em 2025.

Configuração Moderna de i18n no Next.js

A abordagem integrada do Next.js para i18n se tornou mais robusta:

// next.config.js em 2025
/** @type {import('next').NextConfig} */
const nextConfig = {
  i18n: {
    // Idiomas suportados
    locales: ['pt-BR', 'en-US', 'es', 'fr', 'zh-CN'],
    
    // Idioma padrão
    defaultLocale: 'pt-BR',
    
    // Novos recursos em 2025
    localeDetection: true,      // Detecção automática de idioma
    automaticLocalePrefix: true, // Novo em 2025 - prefixos de URL simplificados
    
    // Domínios específicos por idioma (para SEO otimizado)
    domains: [
      {
        domain: 'meuapp.com.br',
        defaultLocale: 'pt-BR',
      },
      {
        domain: 'myapp.com',
        defaultLocale: 'en-US',
      },
      {
        domain: 'miapp.es',
        defaultLocale: 'es',
      },
    ],
    
    // Novo em 2025: estratégia de fallback para conteúdo parcialmente traduzido
    fallbackStrategy: 'partial',
    
    // Padrões de URL que não devem ser traduzidos 
    excludeFromTranslation: [
      '/api/*',
      '/admin/*',
      '/static/*',
    ],
  },
  
  // Outras configurações do Next.js
};

module.exports = nextConfig;

Utilizando Traduções em Componentes Server e Client

O Next.js App Router introduziu uma clara separação entre componentes de servidor e cliente, que requer abordagens específicas para i18n:

// Exemplo para componentes Server no Next.js App Router

// Em /app/[lang]/layout.tsx
import { Locale } from '@/i18n/config';
import { getTranslations } from '@/i18n/server';

export default async function RootLayout({
  children,
  params: { lang }
}: {
  children: React.ReactNode;
  params: { lang: Locale };
}) {
  // Obter traduções no servidor
  const { t } = await getTranslations(lang, 'common');
  
  return (
    <html lang={lang}>
      <body>
        <header>
          <h1>{t('site.title')}</h1>
          <nav>
            <ul>
              <li>{t('nav.home')}</li>
              <li>{t('nav.products')}</li>
              <li>{t('nav.contact')}</li>
            </ul>
          </nav>
        </header>
        <main>{children}</main>
        <footer>{t('site.footer')}</footer>
      </body>
    </html>
  );
}

// Para componentes Client
'use client';

import { useTranslation } from '@/i18n/client';
import { useParams } from 'next/navigation';

export default function LanguageSwitcher() {
  const params = useParams();
  const lang = params.lang as Locale;
  const { t } = useTranslation(lang, 'common');
  
  return (
    <div className="language-selector">
      <p>{t('language.select')}</p>
      <select>
        <option value="pt-BR">{t('language.portuguese')}</option>
        <option value="en-US">{t('language.english')}</option>
        <option value="es">{t('language.spanish')}</option>
      </select>
    </div>
  );
}

Implementação de uma solução completa

Para mostrar como tudo se junta, aqui está uma implementação mais completa no App Router do Next.js:

// Em /i18n/config.ts
export const defaultLocale = 'pt-BR';
export const locales = ['pt-BR', 'en-US', 'es', 'fr', 'zh-CN'] as const;
export type Locale = typeof locales[number];

// Em /i18n/server.ts
import { createInstance } from 'i18next';
import resourcesToBackend from 'i18next-resources-to-backend';
import { initReactI18next } from 'react-i18next/initReactI18next';
import { Locale, defaultLocale } from './config';

export async function getTranslations(locale: Locale, namespace: string) {
  const i18nInstance = createInstance();
  await i18nInstance
    .use(initReactI18next)
    .use(resourcesToBackend((language: string, ns: string) => 
      import(`./locales/${language}/${ns}.json`)))
    .init({
      lng: locale,
      fallbackLng: defaultLocale,
      supportedLngs: locales,
      defaultNS: 'common',
      ns: namespace,
      fallbackNS: 'common',
    });

  return {
    t: i18nInstance.getFixedT(locale, namespace),
    i18n: i18nInstance
  };
}

// Em /middleware.ts - para roteamento e redirecionamento baseado em idioma
import { NextRequest, NextResponse } from 'next/server';
import { match as matchLocale } from '@formatjs/intl-localematcher';
import Negotiator from 'negotiator';
import { locales, defaultLocale } from './i18n/config';

function getLocale(request: NextRequest): string {
  // Simulando cabeçalhos para o negotiator
  const headers = {
    'accept-language': request.headers.get('accept-language') || defaultLocale
  };

  const languages = new Negotiator({ headers }).languages();
  
  // Usar @formatjs/intl-localematcher para escolher o melhor idioma
  const locale = matchLocale(languages, locales, defaultLocale);
  
  return locale;
}

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname;
  
  // Verificar se a URL já inclui uma localidade
  const pathnameHasLocale = locales.some(
    locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );

  if (pathnameHasLocale) return NextResponse.next();

  // Redirecionar se a localidade não estiver na URL
  const locale = getLocale(request);
  const newUrl = new URL(`/${locale}${pathname}`, request.url);
  
  return NextResponse.redirect(newUrl);
}

export const config = {
  matcher: [
    // Excluir arquivos estáticos e API
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

Desenvolvimento

Adicione o conteúdo principal aqui.

Conclusão

Finalize o post com suas conclusões.

React Next.js Internacionalização i18n Frontend

Compartilhe este artigo

Transforme suas ideias em realidade

Vamos trabalhar juntos para criar soluções inovadoras que impulsionem seu negócio.