Internacionalização com I18n em projetos NextJs
O conteúdo deste post foi inspirado na solução que fiz para o meu site
Neste artigo tentarei ser breve e ensinar de forma rápida e clara como adicionar internacionalização em um projeto NextJs, seguindo o exemplo utilizado em meu próprio website.
Para este tutorial, utilizaremos do app router sem a utilização de roteamento i18n (isto é, a tradução não estará amarrada à rota, exemplo: "example.com/en")
Além disso, estaremos utilizando de um cookie para que o website possa ser entregue na linguagem correta ao usuário, este cookie será definido no primeiro carregamento do usuário ao website e poderá ser redefinido posteriormente por um componente seletor.
Setup do projeto
Caso ainda não tenha feito, inicie seu projeto Next.Js com a configuração de App Router.
Em seguida, instale o pacote next-intl
npm install next-intl
Em seguida, criaremos a seguinte estrutura de arquivos
├── i18n
├── dictionaries
├── pt.json (1)
└── ...
├── locales.ts (2)
├── request.ts (3)
└── setLocale.ts (4)
├── next.config.mjs (5)
└── app
├── layout.tsx (6)
└── page.tsx (7)
i18n/dictionaries/pt.json
A pasta i18n guardará tudo referente à localização e internacionalização para facilitar a estruturação do projeto.
A pasta dictionaries guardará nossas traduções. Estes arquivos podem ser carregados de forma estática ou dinâmica. Para a simplicidade, estaremos adicionando como um arquivo.
i18n/dictionaries/pt.json
{
"HomePage": {
"title": "Olá mundo!"
}
}
i18n/locales.ts
Para que possamos guardar nossas constantes sobre a localização. (Este também pode ser guardado de forma dinâmica ou mesmo se tornar uma variável de ambiente)
i18n/locales.ts
export const SUPPORTED_LOCALES = ['en', 'pt']; // Traduções disponívels
export const DEFAULT_LOCALE = 'en'; // Idioma padrão utilizado como fallback
export const LOCALE_COOKIE_NAME = 'NEXT_LOCALE'; // Chave do cookie que utilizaremos para receber a tradução do servidor
i18n/request.ts
O next-intl utilizará da request para definir como proceder com componentes de servidor que utilizam da internacionalização, o arquivo de request trata da lógica para determinar a linguagem. A nossa abordagem aqui será a de utilizar dos cookies para observar a linguagem a ser utilizada.
i18n/request.ts
import { getRequestConfig } from 'next-intl/server';
import { cookies } from 'next/headers';
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from './locales';
export default getRequestConfig(async () => {
const cookieLocale = cookies().get('NEXT_LOCALE')?.value;
// Determina o locale
const locale = cookieLocale || DEFAULT_LOCALE;
// Verifica se é suportado ou retorna o padrão
const finalLocale = SUPPORTED_LOCALES.includes(locale)
? locale
: DEFAULT_LOCALE;
return {
locale: finalLocale,
messages: (await import(`./dictionaries/${finalLocale}.json`)).default,
};
});
i18n/setLocale.ts
Este arquivo servirá de utilitário para que possamos definir os cookies do lado do servidor.
i18n/setLocale.ts
'use server';
import { cookies } from 'next/headers';
import { LOCALE_COOKIE_NAME } from './locales';
export async function setUserLocale(locale: string) {
cookies().set(LOCALE_COOKIE_NAME, locale);
}
next.config.mjs
Agora, configure o plug-in que cria um alias para fornecer uma configuração i18n específica da solicitação aos componentes do servidor. (que criamos como i18n/request.ts
)
next.config.mjs
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default withNextIntl(nextConfig);
app/layout.tsx
O código de idioma fornecido em i18n/request.ts
está disponível via getLocale
e pode ser usado para configurar o idioma do documento. Além disso, podemos usar este local para passar a configuração de i18n/request.ts
para componentes do cliente via NextIntlClientProvider
.
app/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { getLocale, getMessages } from 'next-intl/server';
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const locale = await getLocale();
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
app/page.tsx
Para utilizar suas traduções, basta usar o hook useTranslations
em seus componentes e páginas!
app/page.tsx
import { useTranslations } from 'next-intl';
export default function HomePage() {
const t = useTranslations('HomePage');
return <h1>{t('title')}</h1>;
}
Alterando a linguagem no primeiro carregamento
Embora a partir do setup o seu projeto já funcione com a internacionalização, para que seja possível alterar a linguagem é necessário que criemos um componente client-side que possa ler a linguagem do navegador e alterar o cookie. Para isso, basta criar um componente da seguinte forma:
import { useEffect } from 'react';
import {
SUPPORTED_LOCALES,
DEFAULT_LOCALE,
LOCALE_COOKIE_NAME,
} from '../../i18n/locales';
import { setUserLocale } from '../../i18n/setLocale';
export default function LocaleSetter() {
useEffect(() => {
// Verifica se o cookie de locale já existe
if (document.cookie.includes(LOCALE_COOKIE_NAME)) return;
// Remove o país do locale (e.g. pt-BR e pt-PT se torna pt)
const navigatorLang = navigator.language.split('-')[0];
let locale = SUPPORTED_LOCALES.includes(navigatorLang)
? navigatorLang
: DEFAULT_LOCALE;
// Altera cookie
setUserLocale(locale);
}, []);
return null;
}
Assim, podemos incluir o LocaleSetter
em nosso app/layout.tsx
e nossa aplicação já contará com traduções definidas pelo idioma do navegador!
A partir daqui, é possível também criar um componente seletor e utilizar da função setUserLocale
para que a linguagem seja redefinida!
Até a próxima ;D