Começando a Internacionalização (i18n) com Intlayer, Vite e React
O que é Intlayer?
Intlayer é uma biblioteca inovadora de internacionalização (i18n), de código aberto, projetada para simplificar o suporte a múltiplos idiomas em aplicações web modernas.
Com o Intlayer, você pode:
- Gerenciar traduções facilmente utilizando dicionários declarativos no nível do componente.
- Localizar dinamicamente metadados, rotas e conteúdo.
- Garantir suporte TypeScript com tipos gerados automaticamente, melhorando a autocompletação e a detecção de erros.
- Beneficiar-se de recursos avançados, como detecção e mudança dinâmica de localidade.
Guia Passo a Passo para Configurar Intlayer em uma Aplicação Vite e React
Passo 1: Instalar Dependências
Instale os pacotes necessários usando npm:
1npm install intlayer react-intlayer
1yarn add intlayer react-intlayer
1pnpm add intlayer react-intlayer
Passo 2: Configuração do seu projeto
Crie um arquivo de configuração para configurar os idiomas da sua aplicação:
1// intlayer.config.ts
2
3import { Locales, type IntlayerConfig } from "intlayer";
4
5const config: IntlayerConfig = {
6 internationalization: {
7 locales: [
8 Locales.PORTUGUESE,
9 Locales.FRENCH,
10 Locales.SPANISH,
11 // Seus outros idiomas
12 ],
13 defaultLocale: Locales.PORTUGUESE,
14 },
15};
16
17export default config;
Para ver todos os parâmetros disponíveis, consulte a documentação de configuração aqui.
Passo 3: Integrar Intlayer na sua Configuração Vite
Adicione o plugin intlayer na sua configuração.
1import { defineConfig } from "vite";
2import react from "@vitejs/plugin-react-swc";
3import { intLayerPlugin } from "react-intlayer/vite";
4
5// https://vitejs.dev/config/
6export default defineConfig({
7 plugins: [react(), intLayerPlugin()],
8});
Passo 4: Declare Seu Conteúdo
Crie e gerencie seus dicionários de conteúdo:
1// src/app.content.tsx
2import { t, type DeclarationContent } from "intlayer";
3import { type ReactNode } from "react";
4
5const appContent = {
6 key: "app",
7 content: {
8 viteLogo: t({
9 en: "Vite logo",
10 fr: "Logo Vite",
11 es: "Logo Vite",
12 }),
13 reactLogo: t({
14 en: "React logo",
15 fr: "Logo React",
16 es: "Logo React",
17 }),
18
19 title: "Vite + React",
20
21 count: t({
22 en: "count is ",
23 fr: "le compte est ",
24 es: "el recuento es ",
25 }),
26
27 edit: t<ReactNode>({
28 // Não se esqueça de importar React se você usar um nó React em seu conteúdo
29 en: (
30 <>
31 Edit <code>src/App.tsx</code> and save to test HMR
32 </>
33 ),
34 fr: (
35 <>
36 Éditez <code>src/App.tsx</code> et enregistrez pour tester HMR
37 </>
38 ),
39 es: (
40 <>
41 Edita <code>src/App.tsx</code> y guarda para probar HMR
42 </>
43 ),
44 }),
45
46 readTheDocs: t({
47 en: "Click on the Vite and React logos to learn more",
48 fr: "Cliquez sur les logos Vite et React pour en savoir plus",
49 es: "Haga clic en los logotipos de Vite y React para obtener más información",
50 }),
51 },
52} satisfies DeclarationContent;
53
54export default appContent;
Nota: Se seu arquivo de conteúdo incluir código TSX, você deve considerar importar import React from "react"; em seu arquivo de conteúdo.
Veja como declarar seus arquivos de declaração do Intlayer.
Passo 5: Utilize Intlayer no Seu Código
Acesse seus dicionários de conteúdo em toda a sua aplicação:
1import { useState } from "react";
2import reactLogo from "./assets/react.svg";
3import viteLogo from "/vite.svg";
4import "./App.css";
5import { LocaleSwitcher } from "./components/LangSwitcherDropDown";
6import { IntlayerProvider, useIntlayer } from "react-intlayer";
7
8function AppContent() {
9 const [count, setCount] = useState(0);
10 const content = useIntlayer("app");
11
12 return (
13 <>
14 <div>
15 <a href="https://vitejs.dev" target="_blank">
16 <img src={viteLogo} className="logo" alt={content.viteLogo.value} />
17 </a>
18 <a href="https://react.dev" target="_blank">
19 <img
20 src={reactLogo}
21 className="logo react"
22 alt={content.reactLogo.value}
23 />
24 </a>
25 </div>
26 <h1>{content.title}</h1>
27 <div className="card">
28 <button onClick={() => setCount((count) => count + 1)}>
29 {content.count}
30 {count}
31 </button>
32 <p>{content.edit}</p>
33 </div>
34 <p className="read-the-docs">{content.readTheDocs}</p>
35 <div className="absolute bottom-5 right-5 z-50">
36 <LocaleSwitcher />
37 </div>
38 </>
39 );
40}
41
42function App() {
43 return (
44 <IntlayerProvider>
45 <AppContent />
46 </IntlayerProvider>
47 );
48}
49
50export default App;
Nota: Se você quiser usar seu conteúdo em um atributo string, como alt, title, href, aria-label, etc., você deve chamar o valor da função, como:
tsx1<img src={content.image.src.value} alt={content.image.value} />
(Opcional) Passo 6: Mudar o idioma do seu conteúdo
Para mudar o idioma do seu conteúdo, você pode usar a função setLocale fornecida pelo hook useLocale. Esta função permite que você defina a localidade da aplicação e atualize o conteúdo de acordo.
1import { Locales } from "intlayer";
2import { useLocale } from "react-intlayer";
3
4const LocaleSwitcher = () => {
5 const { setLocale } = useLocale();
6
7 return (
8 <button onClick={() => setLocale(Locales.PORTUGUESE)}>
9 Mudar idioma para Português
10 </button>
11 );
12};
(Opcional) Passo 7: Adicionar Roteamento Localizado à sua aplicação
O objetivo deste passo é criar rotas únicas para cada idioma. Isso é útil para SEO e URLs amigáveis. Exemplo:
1// /painel
2// /es/painel
3// /fr/painel
Por padrão, as rotas não são prefixadas para a localidade padrão. Se você quiser prefixar a localidade padrão, pode definir a opção middleware.prefixDefault como true em sua configuração. Consulte a documentação de configuração para mais informações.
Para adicionar roteamento localizado à sua aplicação, você pode criar um componente LocaleRouter que encapsula as rotas da sua aplicação e lida com o roteamento baseado na localidade. Aqui está um exemplo usando React Router:
1// Importando dependências e funções necessárias
2import { Locales, getConfiguration, getPathWithoutLocale } from "intlayer"; // Funções e tipos utilitários de 'intlayer'
3import { FC, PropsWithChildren } from "react"; // Tipos React para componentes funcionais e props
4import { IntlayerProvider } from "react-intlayer"; // Provedor para contexto de internacionalização
5import {
6 BrowserRouter,
7 Routes,
8 Route,
9 useParams,
10 Navigate,
11 useLocation,
12} from "react-router-dom"; // Componentes Router para gerenciar navegação
13
14// Desestruturando a configuração do Intlayer
15const { internationalization, middleware } = getConfiguration();
16const { locales, defaultLocale } = internationalization;
17
18/**
19 * Um componente que lida com localização e encapsula os filhos com o contexto de localidade apropriado.
20 * Ele gerencia a detecção e validação de URL com base na localidade.
21 */
22const AppLocalized: FC<PropsWithChildren> = ({ children }) => {
23 const path = useLocation().pathname; // Obtém o caminho da URL atual
24 const { locale } = useParams<{ locale: Locales }>(); // Extrai o parâmetro de localidade da URL
25
26 // Determina a localidade atual, retornando à padrão se não fornecido
27 const currentLocale = locale ?? defaultLocale;
28
29 // Remove o prefixo de localidade do caminho para construir um caminho base
30 const pathWithoutLocale = getPathWithoutLocale(
31 path // Caminho da URL atual
32 );
33
34 /**
35 * Se middleware.prefixDefault for verdadeiro, a localidade padrão deve sempre ser prefixada.
36 */
37 if (middleware.prefixDefault) {
38 // Valida a localidade
39 if (!locale || !locales.includes(locale)) {
40 // Redireciona para a localidade padrão com o caminho atualizado
41 return (
42 <Navigate
43 to={`/${defaultLocale}/${pathWithoutLocale}`}
44 replace // Substituir a entrada de histórico atual pela nova
45 />
46 );
47 }
48
49 // Encapsula os filhos com o IntlayerProvider e define a localidade atual
50 return (
51 <IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
52 );
53 } else {
54 /**
55 * Quando middleware.prefixDefault é falso, a localidade padrão não é prefixada.
56 * Garante que a localidade atual seja válida e não a localidade padrão.
57 */
58 if (
59 currentLocale.toString() !== defaultLocale.toString() &&
60 !locales
61 .filter(
62 (locale) => locale.toString() !== defaultLocale.toString() // Exclui a localidade padrão
63 )
64 .includes(currentLocale) // Verifica se a localidade atual está na lista de localidades válidas
65 ) {
66 // Redireciona para o caminho sem o prefixo da localidade
67 return <Navigate to={pathWithoutLocale} replace />;
68 }
69
70 // Encapsula os filhos com o IntlayerProvider e define a localidade atual
71 return (
72 <IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
73 );
74 }
75};
76
77/**
78 * Um componente de roteador que configura rotas específicas de localidade.
79 * Utiliza o React Router para gerenciar navegação e renderizar componentes localizados.
80 */
81export const LocaleRouter: FC<PropsWithChildren> = ({ children }) => (
82 <BrowserRouter>
83 <Routes>
84 <Route
85 // Padrão da rota para capturar a localidade (por exemplo, /pt/, /fr/) e combinar todos os caminhos subsequentes
86 path="/:locale/*"
87 element={<AppLocalized>{children}</AppLocalized>} // Encapsula filhos com gerenciamento de localidade
88 />
89
90 {
91 // Se o prefixo da localidade padrão estiver desativado, renderiza os filhos diretamente no caminho raiz
92 !middleware.prefixDefault && (
93 <Route
94 path="*"
95 element={<AppLocalized>{children}</AppLocalized>} // Encapsula filhos com gerenciamento de localidade
96 />
97 )
98 }
99 </Routes>
100 </BrowserRouter>
101);
Em paralelo, você também pode usar o intLayerMiddlewarePlugin para adicionar roteamento do lado do servidor à sua aplicação. Este plugin irá detectar automaticamente a localidade atual com base na URL e definir o cookie de localidade apropriado. Se nenhuma localidade for especificada, o plugin determinará a localidade mais apropriada com base nas preferências de idioma do navegador do usuário. Se nenhuma localidade for detectada, ele redirecionará para a localidade padrão.
1import { defineConfig } from "vite";
2import react from "@vitejs/plugin-react-swc";
3import { intLayerPlugin, intLayerMiddlewarePlugin } from "react-intlayer/vite";
4
5// https://vitejs.dev/config/
6export default defineConfig({
7 plugins: [react(), intLayerPlugin(), intLayerMiddlewarePlugin()],
8});
(Opcional) Passo 8: Mudar a URL quando a localidade mudar
Para mudar a URL quando a localidade mudar, você pode usar a prop onLocaleChange fornecida pelo hook useLocale. Em paralelo, você pode usar os hooks useLocation e useNavigate do react-router-dom para atualizar o caminho da URL.
1import { Locales, getLocalizedUrl } from "intlayer";
2import { useLocale } from "react-intlayer";
3import { useLocation, useNavigate } from "react-router-dom";
4
5const LocaleSwitcher = () => {
6 const location = useLocation(); // Obtém o caminho da URL atual. Exemplo: /fr/sobre
7 const navigate = useNavigate();
8
9 const changeUrl = (locale: Locales) => {
10 // Constrói a URL com a localidade atualizada
11 // Exemplo: /es/sobre com a localidade definida para espanhol
12 const pathWithLocale = getLocalizedUrl(location.pathname, locale);
13
14 // Atualiza o caminho da URL
15 navigate(pathWithLocale);
16 };
17
18 const { setLocale } = useLocale({
19 onLocaleChange: changeUrl,
20 });
21
22 return (
23 <button onClick={() => setLocale(Locales.PORTUGUESE)}>
24 Mudar idioma para Português
25 </button>
26 );
27};
Configurar TypeScript
Intlayer usa aumento de módulo para obter benefícios do TypeScript e tornar seu código mais forte.
Certifique-se de que sua configuração TypeScript inclua os tipos gerados automaticamente.
1// tsconfig.json
2
3{
4 // sua configuração personalizada
5 include: [
6 "src",
7 "types", // <- Inclua os tipos gerados automaticamente
8 ],
9}
Configuração do Git
É recomendável ignorar os arquivos gerados pelo Intlayer. Isso permite que você evite contabilizá-los em seu repositório Git.
Para fazer isso, você pode adicionar as seguintes instruções ao seu arquivo .gitignore:
1# Ignore os arquivos gerados pelo Intlayer
2.intlayer
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