Começando com Internacionalização (i18n) usando Intlayer e Next.js com Page Router

    O que é o Intlayer?

    Intlayer é uma biblioteca de internacionalização (i18n) inovadora e de código aberto, projetada para simplificar o suporte multilíngue em aplicações web modernas. O Intlayer se integra perfeitamente com o mais recente framework Next.js, incluindo seu tradicional Page Router.

    Com o Intlayer, você pode:

    • Gerenciar traduções facilmente usando dicionários declarativos no nível do componente.
    • Localizar dinamicamente metadados, rotas e conteúdo.
    • Garantir suporte ao TypeScript com tipos autogerados, melhorando a autocompletação e a detecção de erros.
    • Aproveitar recursos avançados, como detecção e troca dinâmica de localidade.

    O Intlayer é compatível com Next.js 12, 13, 14 e 15. Se você estiver usando o Next.js App Router, consulte o guia do App Router. Para o Next.js 15, siga este guia.


    Guia Passo a Passo para Configurar o Intlayer em uma Aplicação Next.js Usando Page Router

    Passo 1: Instale as Dependências

    Instale os pacotes necessários usando seu gerenciador de pacotes preferido:

    bash
    npm install intlayer next-intlayer
    • intlayer

      O pacote principal que fornece ferramentas de internacionalização para gerenciamento de configuração, tradução, declaração de conteúdo, transpiração e comandos CLI.

    • next-intlayer

      O pacote que integra o Intlayer com o Next.js. Ele fornece provedores de contexto e hooks para internacionalização no Next.js. Além disso, inclui o plugin Next.js para integrar o Intlayer com Webpack ou Turbopack, bem como middleware para detectar a localidade preferida do usuário, gerenciar cookies e lidar com redirecionamento de URLs.

    Passo 2: Configure Seu Projeto

    Crie um arquivo de configuração para definir os idiomas suportados pela sua aplicação:

    intlayer.config.ts
    // Configuração do Intlayerimport { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [      Locales.ENGLISH,      Locales.FRENCH,      Locales.SPANISH,      // Adicione outras localidades aqui    ],    defaultLocale: Locales.ENGLISH,  },};export default config;

    Por meio deste arquivo de configuração, você pode configurar URLs localizados, redirecionamento de middleware, nomes de cookies, a localização e extensão de suas declarações de conteúdo, desativar logs do Intlayer no console e muito mais. Para uma lista completa de parâmetros disponíveis, consulte a documentação de configuração.

    Passo 3: Integre o Intlayer com a Configuração do Next.js

    Modifique sua configuração do Next.js para incorporar o Intlayer:

    next.config.mjs
    // Integração do Intlayer com Next.jsimport { withIntlayer } from "next-intlayer/server";/** @type {import('next').NextConfig} */const nextConfig = {  // Sua configuração existente do Next.js};export default withIntlayer(nextConfig);

    O plugin withIntlayer() do Next.js é usado para integrar o Intlayer com o Next.js. Ele garante a construção de arquivos de declaração de conteúdo e os monitora no modo de desenvolvimento. Define variáveis de ambiente do Intlayer nos ambientes Webpack ou Turbopack. Além disso, fornece aliases para otimizar o desempenho e garante compatibilidade com componentes do servidor.

    Passo 4: Configure Middleware para Detecção de Localidade

    Configure o middleware para detectar e lidar automaticamente com a localidade preferida do usuário:

    src/middleware.ts
    // Middleware para detecção de localidadeexport { intlayerMiddleware as middleware } from "next-intlayer/middleware";export const config = {  matcher:    "/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\..*|_next).*)",};

    Adapte o parâmetro matcher para corresponder às rotas da sua aplicação. Para mais detalhes, consulte a documentação do Next.js sobre configuração do matcher.

    Passo 5: Defina Rotas Dinâmicas de Localidade

    Implemente roteamento dinâmico para servir conteúdo localizado com base na localidade do usuário.

    1. Crie Páginas Específicas para Localidades:

      Renomeie o arquivo da sua página principal para incluir o segmento dinâmico [locale].

      bash
      mv src/pages/index.tsx src/pages/[locale]/index.tsx
    2. Atualize _app.tsx para Lidar com Localização:

      Modifique seu `_app.tsx` para incluir provedores do Intlayer. ```tsx fileName="src/pages/_app.tsx" codeFormat="typescript"

      // Configuração do App com Intlayer import type { FC } from "react"; import type { AppProps } from "next/app"; import { IntlayerClientProvider } from "next-intlayer";

      const App = FC<AppProps>({ Component, pageProps }) => { const { locale } = pageProps; return ( <IntlayerClientProvider locale={locale}> <Component {...pageProps} /> </IntlayerClientProvider> ); } export default MyApp; ``` ```jsx fileName="src/pages/_app.mjx" codeFormat="esm"

      // Configuração do App com Intlayer import { IntlayerClientProvider } from "next-intlayer";

      const App = ({ Component, pageProps }) => ( <IntlayerClientProvider locale={locale}> <Component {...pageProps} /> </IntlayerClientProvider> ); export default App; ``` ```jsx fileName="src/pages/_app.csx" codeFormat="commonjs"

      // Configuração do App com Intlayer const { IntlayerClientProvider } = require("next-intlayer");

      const App = ({ Component, pageProps }) => ( <IntlayerClientProvider locale={locale}> <Component {...pageProps} /> </IntlayerClientProvider> ); module.exports = App; ```
    3. Configure getStaticPaths e getStaticProps:

      No seu `[locale]/index.tsx`, defina os caminhos e as propriedades para lidar com diferentes localidades. ```tsx fileName="src/pages/[locale]/index.tsx" codeFormat="typescript"

      // Configuração de rotas dinâmicas import type { FC } from "react"; import type { GetStaticPaths, GetStaticProps } from "next"; import { type Locales, getConfiguration } from "intlayer";

    const HomePage: FC = () =>

    {/ Seu conteúdo aqui /}
    ;

    export const getStaticPaths: GetStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps: GetStaticProps = ({ params }) => { const locale = params?.locale as string; return { props: { locale, }, }; }; export default HomePage; ``` ```jsx fileName="src/pages/[locale]/index.mjx" codeFormat="esm" import { getConfiguration } from "intlayer"; import { ComponentExample } from "@components/ComponentExample"; const HomePage = () => <div>{/* Seu conteúdo aqui */}</div>; export const getStaticPaths = () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; export const getStaticProps = ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; ``` ```jsx fileName="src/pages/[locale]/index.csx" codeFormat="commonjs" const { getConfiguration } = require("intlayer"); const { ComponentExample } = require("@components/ComponentExample"); const HomePage = () => <div>{/* Seu conteúdo aqui */}</div>; const getStaticPaths = async () => { const { internationalization } = getConfiguration(); const { locales } = internationalization; const paths = locales.map((locale) => ({ params: { locale }, })); return { paths, fallback: false }; }; const getStaticProps = async ({ params }) => { const locale = params?.locale; return { props: { locale, }, }; }; module.exports = { getStaticProps, getStaticPaths, default: HomePage, }; ```

    getStaticPaths e getStaticProps garantem que sua aplicação pré-construa as páginas necessárias para todos os idiomas no Next.js Page Router. Essa abordagem reduz o cálculo em tempo de execução e melhora a experiência do usuário. Para mais detalhes, consulte a documentação do Next.js sobre getStaticPaths e getStaticProps.

    Passo 6: Declare Seu Conteúdo

    Crie e gerencie suas declarações de conteúdo para armazenar traduções.

    src/pages/[locale]/home.content.ts
    import { t, type Dictionary } from "intlayer";const homeContent = {  key: "home",  content: {    title: t({      pt: "Bem-vindo ao Meu Site",      en: "Welcome to My Website",      fr: "Bienvenue sur mon site Web",      es: "Bienvenido a mi sitio web",    }),    description: t({      pt: "Comece editando esta página.",      en: "Get started by editing this page.",      fr: "Commencez par éditer cette page.",      es: "Comience por editar esta página.",    }),  },} satisfies Dictionary;export default homeContent;

    Para mais informações sobre como declarar conteúdo, consulte o guia de declaração de conteúdo.

    Passo 7: Utilize o Conteúdo no Seu Código

    Acesse seus dicionários de conteúdo em toda a aplicação para exibir o conteúdo traduzido.

    src/pages/[locale]/index.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";import { ComponentExample } from "@components/ComponentExample";const HomePage: FC = () => {  const content = useIntlayer("home");  return (    <div>      <h1>{content.title}</h1>      <p>{content.description}</p>      <ComponentExample />      {/* Componentes adicionais */}    </div>  );};// ... Resto do código, incluindo getStaticPaths e getStaticPropsexport default HomePage;
    src/components/ComponentExample.tsx
    import type { FC } from "react";import { useIntlayer } from "next-intlayer";export const ComponentExample: FC = () => {  const content = useIntlayer("component-example"); // Certifique-se de ter uma declaração de conteúdo correspondente  return (    <div>      <h2>{content.title}</h2>      <p>{content.content}</p>    </div>  );};

    Ao usar traduções em atributos string (por exemplo, alt, title, href, aria-label), chame o valor da função da seguinte forma:

    jsx
    <img src={content.image.src.value} alt={content.image.value} />

    Para saber mais sobre o hook useIntlayer, consulte a documentação.

    (Opcional) Etapa 8: Internacionalizar Seus Metadados

    Para internacionalizar metadados como títulos e descrições de páginas, use a função getStaticProps em conjunto com a função getTranslation do Intlayer.

    src/pages/[locale]/index.tsx
    import { GetStaticPaths, GetStaticProps } from "next";import { type IConfigLocales, getTranslation, Locales } from "intlayer";import { useIntlayer } from "next-intlayer";interface HomePageProps {  locale: string;  metadata: Metadata;}const HomePage = ({ metadata }: HomePageProps) => {  // Os metadados podem ser usados no head ou em outros componentes conforme necessário  return (    <div>      <Head>        <title>{metadata.title}</title>        <meta name="description" content={metadata.description} />      </Head>      {/* Conteúdo adicional */}    </div>  );};export const getStaticProps: GetStaticProps = async ({ params }) => {  const locale = params?.locale as string;  const t = <T,>(content: IConfigLocales<T>) => getTranslation(content, locale);  const metadata = {    title: t({      pt: "Meu Site",      en: "My Website",      fr: "Mon Site Web",      es: "Mi Sitio Web",    }),    description: t({      pt: "Bem-vindo ao meu site.",      en: "Welcome to my website.",      fr: "Bienvenue sur mon site Web.",      es: "Bienvenido a mi sitio web.",    }),  };  return {    props: {      locale,      metadata,    },  };};export default HomePage;// ... Resto do código incluindo getStaticPaths

    (Opcional) Etapa 9: Alterar o Idioma do Seu Conteúdo

    Para permitir que os usuários alterem o idioma dinamicamente, use a função setLocale fornecida pelo hook useLocale.

    src/components/LanguageSwitcher.tsx
    import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocalePageRouter } from "next-intlayer";import { type FC } from "react";import Link from "next/link";const LocaleSwitcher: FC = () => {  const { locale, pathWithoutLocale, availableLocales, setLocale } =    useLocalePageRouter();  return (    <div>      <button popoverTarget="localePopover">{getLocaleName(locale)}</button>      <div id="localePopover" popover="auto">        {availableLocales.map((localeItem) => (          <Link            href={getLocalizedUrl(pathWithoutLocale, localeItem)}            hrefLang={localeItem}            key={localeItem}            aria-current={locale === localeItem ? "page" : undefined}            onClick={(e) => {              e.preventDefault();              setLocale(localeItem);            }}          >            <span>              {/* Locale - ex.: FR */}              {localeItem}            </span>            <span>              {/* Idioma no próprio Locale - ex.: Français */}              {getLocaleName(localeItem, locale)}            </span>            <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>              {/* Idioma no Locale atual - ex.: Francés com o locale atual definido como Locales.SPANISH */}              {getLocaleName(localeItem)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* Idioma em Inglês - ex.: French */}              {getLocaleName(localeItem, Locales.ENGLISH)}            </span>          </Link>        ))}      </div>    </div>  );};

    A API useLocalePageRouter é a mesma que useLocale. Para saber mais sobre o hook useLocale, consulte a documentação.

    Referências da documentação:

    Para garantir que a navegação da sua aplicação respeite a localidade atual, você pode criar um componente Link personalizado. Este componente automaticamente adiciona um prefixo aos URLs internos com o idioma atual. Por exemplo, quando um usuário que fala francês clica em um link para a página "Sobre", ele é redirecionado para /fr/about em vez de /about.

    Esse comportamento é útil por várias razões:

    • SEO e Experiência do Usuário: URLs localizados ajudam os motores de busca a indexar corretamente as páginas específicas de idioma e fornecem aos usuários conteúdo no idioma preferido.
    • Consistência: Usando um link localizado em toda a aplicação, você garante que a navegação permaneça na localidade atual, evitando mudanças inesperadas de idioma.
    • Manutenibilidade: Centralizar a lógica de localização em um único componente simplifica o gerenciamento de URLs, tornando sua base de código mais fácil de manter e expandir à medida que sua aplicação cresce.

    Abaixo está a implementação de um componente Link localizado em TypeScript:

    src/components/Link.tsx
    "use client";import { getLocalizedUrl } from "intlayer";import NextLink, { type LinkProps as NextLinkProps } from "next/link";import { useLocale } from "next-intlayer";import { forwardRef, PropsWithChildren, type ForwardedRef } from "react";/** * Função utilitária para verificar se um URL é externo. * Se o URL começar com http:// ou https://, ele é considerado externo. */export const checkIsExternalLink = (href?: string): boolean =>  /^https?:///.test(href ?? "");/** * Um componente de Link personalizado que adapta o atributo href com base na localidade atual. * Para links internos, ele usa `getLocalizedUrl` para prefixar o URL com a localidade (ex.: /fr/about). * Isso garante que a navegação permaneça no mesmo contexto de localidade. */export const Link = forwardRef<  HTMLAnchorElement,  PropsWithChildren<NextLinkProps>>(({ href, children, ...props }, ref: ForwardedRef<HTMLAnchorElement>) => {  const { locale } = useLocale();  const isExternalLink = checkIsExternalLink(href.toString());  // Se o link for interno e um href válido for fornecido, obtenha o URL localizado.  const hrefI18n: NextLinkProps["href"] =    href && !isExternalLink ? getLocalizedUrl(href.toString(), locale) : href;  return (    <NextLink href={hrefI18n} ref={ref} {...props}>      {children}    </NextLink>  );});Link.displayName = "Link";

    Como Funciona

    • Detectando Links Externos:
      A função auxiliar checkIsExternalLink determina se um URL é externo. Links externos permanecem inalterados porque não precisam de localização.

    • Recuperando a Localidade Atual:
      O hook useLocale fornece a localidade atual (ex.: fr para francês).

    • Localizando o URL:
      Para links internos (ou seja, não externos), getLocalizedUrl é usado para prefixar automaticamente o URL com a localidade atual. Isso significa que, se o usuário estiver em francês, passar /about como href será transformado em /fr/about.

    • Retornando o Link:
      O componente retorna um elemento <a> com o URL localizado, garantindo que a navegação seja consistente com a localidade.

    Configurar TypeScript

    O Intlayer utiliza a ampliação de módulos para aproveitar os benefícios do TypeScript e tornar sua base de código mais robusta.

    alt text

    alt text

    Certifique-se de que sua configuração do TypeScript inclua os tipos gerados automaticamente.

    tsconfig.json
    {  // ... Suas configurações existentes do TypeScript  "include": [    // ... Suas configurações existentes do TypeScript    ".intlayer/**/*.ts", // Inclua os tipos gerados automaticamente  ],}

    Configuração do Git

    Para manter seu repositório limpo e evitar o commit de arquivos gerados, é recomendado ignorar os arquivos criados pelo Intlayer.

    Adicione as seguintes linhas ao seu arquivo .gitignore:

    .gitignore
    # Ignore os arquivos gerados pelo Intlayer.intlayer

    Recursos Adicionais

    Seguindo este guia, você pode integrar efetivamente o Intlayer em sua aplicação Next.js usando o Page Router, permitindo suporte robusto e escalável à internacionalização para seus projetos web.

    Avance Mais

    Para ir além, você pode implementar o editor visual ou externalizar seu conteúdo usando o CMS.

    Se você tiver uma ideia para melhorar esta documentação, sinta-se à vontade para contribuir enviando uma pull request no GitHub.

    Link do GitHub para a documentação