Autor:
    Criação:2025-03-25Última atualização:2026-08-25

    Traduza seu site Tanstack Start com Solid.js usando Intlayer | Internacionalização (i18n)

    Índice

    Este guia demonstra como integrar o Intlayer para uma internacionalização perfeita em projetos Tanstack Start com Solid.js, roteamento ciente de localidade, suporte a TypeScript e práticas de desenvolvimento modernas.

    Por que Intlayer em vez de alternativas?

    Comparado com soluções principais como react-i18next ou i18next, Intlayer é uma solução que vem com otimizações integradas como:

    O Intlayer é otimizado para funcionar perfeitamente com TanStack Start e Solid, oferecendo roteamento multilíngue, mapa do site e todos os recursos necessários para dimensionar a internacionalização (i18n).

    Em vez de carregar arquivos JSON enormes em suas páginas, carregue apenas o conteúdo necessário. O Intlayer ajuda a reduzir o tamanho do bundle e das páginas em até 50%.

    Definir o escopo do conteúdo do seu aplicativo facilita a manutenção de aplicativos de grande escala. Você pode duplicar ou excluir uma única pasta de recursos sem o fardo mental de revisar toda a base de código de seu conteúdo. Além disso, o Intlayer é totalmente tipado (fully typed) para garantir a precisão do seu conteúdo.

    A co-localização de conteúdo reduz o contexto necessário pelos Large Language Models (LLMs). O Intlayer também vem com um conjunto de ferramentas, como uma CLI para testar traduções ausentes,LSP, MCP, e agent skills, para tornar a experiência do desenvolvedor (DX) ainda mais tranquila para os agentes de IA.

    Use a automação para traduzir seu pipeline de CI/CD usando o LLM de sua escolha às custas de seu provedor de IA. O Intlayer também oferece um compilador para automatizar a extração de conteúdo, bem como uma plataforma web para ajudar a traduzir em segundo plano.

    Conectar arquivos JSON enormes a componentes pode levar a problemas de desempenho e reatividade. O Intlayer otimiza o carregamento do seu conteúdo no momento da construção.

    Mais do que apenas uma solução i18n, o Intlayer fornece um [editor visual] auto-hospedado(/pt/doc/concept/editor) e um CMS completo para ajudá-lo a gerenciar seu conteúdo multilíngue em tempo real, facilitando a colaboração com tradutores, redatores e outros membros da equipe. O conteúdo pode ser armazenado local e/ou remotamente.


    Guia Passo a Passo para Configurar o Intlayer numa Aplicação Tanstack Start

    www.youtube.com
    ide.intlayer.org
    intlayer-tanstack-start-solid.vercel.app

    Veja o Modelo de Aplicação no GitHub.

    1. Criar o Projeto

      Comece criando um novo projeto TanStack Start seguindo o guia Iniciar novo projeto no site do TanStack Start.

    2. Instalar os Pacotes Intlayer

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

      bash
      npx intlayer init --interactive
      
      a flag --interactive é opcional. Use intlayer-cli init se você for um agente de IA.
      Este comando detectará seu ambiente e instalará os pacotes necessários. Por exemplo:
      bash
      npm install intlayer solid-intlayer
      npm install vite-intlayer --save-dev
      
      • intlayer

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

      • solid-intlayer O pacote que integra o Intlayer com a aplicação Solid. Ele fornece provedores de contexto e hooks para a internacionalização em Solid.

      • vite-intlayer Inclui o plugin Vite para integrar o Intlayer com o bundler Vite, bem como o middleware para detectar a localidade preferida do usuário, gerenciar cookies e lidar com redirecionamento de URL.

    3. Configuração do seu projeto

      Crie um arquivo de configuração para configurar as línguas da sua aplicação:

      intlayer.config.ts
      import type { IntlayerConfig } from "intlayer";
      
      import { Locales } from "intlayer";
      
      const config: IntlayerConfig = {
        internationalization: {
          defaultLocale: Locales.ENGLISH,
          locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
        },
      };
      
      export default config;
      
      Através deste arquivo de configuração, você pode configurar URLs localizadas, redirecionamento de middleware, nomes de cookies, a localização e extensão das 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.
    4. Integrar o Intlayer na sua Configuração Vite

      Adicione o plugin intlayer na sua configuração:

      vite.config.ts
      import { intlayer } from "vite-intlayer";
      import { defineConfig } from "vite";
      import { devtools } from "@tanstack/devtools-vite";
      import { tanstackStart } from "@tanstack/solid-start/plugin/vite";
      import solidPlugin from "vite-plugin-solid";
      
      export default defineConfig({
        plugins: [
          devtools(),
          tanstackStart({
            router: {
              routeFileIgnorePattern:
                ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5|md|mdx|yaml|yml)$",
            },
          }),
          solidPlugin({ ssr: true }),
          intlayer({
            proxy: {
              ignore: (req) => req.url?.startsWith("/api"),
            },
          }),
        ],
      });
      
      O plugin Vite intlayer() é usado para integrar o Intlayer com o Vite. Ele garante a construção dos arquivos de declaração de conteúdo e os monitora no modo de desenvolvimento. Ele define as variáveis de ambiente do Intlayer dentro da aplicação Vite. Adicionalmente, ele fornece aliases para otimizar a performance.
    5. Criar o Layout Raiz

      Configure o seu layout raiz para suportar internacionalização usando useParams para detectar a localidade atual e definindo os atributos lang e dir na tag html.

      src/routes/__root.tsx
      import {
        HeadContent,
        Scripts,
        createRootRouteWithContext,
        getRouteApi,
      } from "@tanstack/solid-router";
      import { HydrationScript } from "solid-js/web";
      import { Suspense, type ParentComponent } from "solid-js";
      import { IntlayerProvider } from "solid-intlayer";
      import { defaultLocale, getHTMLTextDir } from "intlayer";
      
      const localeRoute = getRouteApi("/{-$locale}");
      
      export const Route = createRootRouteWithContext()({
        shellComponent: RootComponent,
      });
      
      const RootComponent: ParentComponent = (props) => {
        const params = localeRoute.useParams();
        const locale = params()?.locale ?? defaultLocale;
      
        return (
          <html dir={getHTMLTextDir(locale)} lang={locale}>
            <head>
              <HydrationScript />
              <HeadContent />
            </head>
            <body>
              <IntlayerProvider locale={locale}>
                <Suspense>{props.children}</Suspense>
              </IntlayerProvider>
              <Scripts />
            </body>
          </html>
        );
      };
      
    6. Criar o Layout de Localidade

      Crie um layout que lide com o prefixo de localidade e realize a validação. Este layout garantirá que apenas localidades válidas sejam processadas.

      Este passo é opcional se você não precisar validar o prefixo de localidade ao nível da rota.
      src/routes/{-$locale}/route.tsx
      import { createFileRoute, Outlet, redirect } from "@tanstack/solid-router";
      import { validatePrefix } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}")({
        beforeLoad: ({ params }) => {
          const localeParam = params.locale;
      
          // Validar o prefixo de localidade
          const { isValid, localePrefix } = validatePrefix(localeParam);
      
          if (!isValid) {
            throw redirect({
              to: "/{-$locale}/404",
              params: { locale: localePrefix },
              replace: true,
            });
          }
        },
        component: Outlet,
      });
      
      Aqui, {-$locale} é um parâmetro de rota dinâmico que é substituído pela localidade atual. Esta notação torna o slot opcional, permitindo que funcione com modos de roteamento como 'prefix-no-default' etc.

      Esteja ciente de que este slot pode causar problemas se você usar múltiplos segmentos dinâmicos na mesma rota (ex: /{-$locale}/outro-caminho/$outroCaminhoDinamico/...). Para o modo 'prefix-all', você pode preferir mudar o slot para $locale. Para o modo 'no-prefix' ou 'search-params', você pode remover o slot inteiramente.

    7. Declarar o Seu Conteúdo

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

      src/contents/page.content.ts
      import type { Dictionary } from "intlayer";
      
      import { t } from "intlayer";
      
      const appContent = {
        content: {
          links: {
            about: t({
              en: "About",
              es: "Acerca de",
              fr: "À propos",
            }),
            home: t({
              en: "Home",
              es: "Inicio",
              fr: "Accueil",
            }),
          },
          meta: {
            title: t({
              en: "Welcome to Intlayer + TanStack Router",
              es: "Bienvenido a Intlayer + TanStack Router",
              fr: "Bienvenue à Intlayer + TanStack Router",
            }),
            description: t({
              en: "This is an example of using Intlayer with TanStack Router",
              es: "Este es un ejemplo de uso de Intlayer con TanStack Router",
              fr: "Ceci est un exemple d'utilisation d'Intlayer avec TanStack Router",
            }),
          },
        },
        key: "app",
      } satisfies Dictionary;
      
      export default appContent;
      
      As suas declarações de conteúdo podem ser definidas em qualquer lugar da sua aplicação, desde que sejam incluídas no diretório contentDir (por padrão, ./app). E correspondam à extensão do arquivo de declaração de conteúdo (por padrão, .content.{json,ts,tsx,js,jsx,mjs,cjs,md,mdx,yaml,yml}).
      Para mais detalhes, consulte a documentação de declaração de conteúdo.
    8. Utilizar Componentes e Hooks Cientes de Localidade

      Crie um componente LocalizedLink para navegação ciente de localidade:

      src/components/LocalizedLink.tsx
      import { Link, type LinkProps } from "@tanstack/solid-router";
      import { getPrefix } from "intlayer";
      import { useLocale } from "solid-intlayer";
      import type { JSX } from "solid-js";
      
      export const LOCALE_ROUTE = "{-$locale}" as const;
      
      export type RemoveLocaleParam<TVal> = TVal extends string
        ? RemoveLocaleFromString<TVal>
        : TVal;
      
      export type To = RemoveLocaleParam<LinkProps["to"]>;
      
      type CollapseDoubleSlashes<TString extends string> =
        TString extends `${infer THead}//${infer TTail}`
          ? CollapseDoubleSlashes<`${THead}/${TTail}`>
          : TString;
      
      export type LocalizedLinkProps = Omit<LinkProps, "to"> & {
        to?: To;
      } & JSX.AnchorHTMLAttributes<HTMLAnchorElement>;
      
      type RemoveAll<
        TString extends string,
        TSub extends string,
      > = TString extends `${infer THead}${TSub}${infer TTail}`
        ? RemoveAll<`${THead}${TTail}`, TSub>
        : TString;
      
      type RemoveLocaleFromString<TString extends string> = CollapseDoubleSlashes<
        RemoveAll<TString, typeof LOCALE_ROUTE>
      >;
      
      export const LocalizedLink = (props: LocalizedLinkProps) => {
        const { locale } = useLocale();
      
        return (
          <Link
            {...props}
            params={{
              locale: getPrefix(locale()).localePrefix,
              ...(typeof props.params === "object" ? props.params : {}),
            }}
            to={`/${LOCALE_ROUTE}${props.to ?? ""}` as LinkProps["to"]}
          />
        );
      };
      

      Este componente tem dois objetivos:

      • Remover o prefixo desnecessário {-$locale} da URL.
      • Injetar o parâmetro de localidade na URL para garantir que o usuário seja diretamente redirecionado para a rota localizada.

      Depois, podemos criar um hook useLocalizedNavigate para navegação programática:

      src/hooks/useLocalizedNavigate.tsx
      import { useNavigate } from "@tanstack/solid-router";
      import { getLocalizedUrl } from "intlayer";
      import { useLocale } from "solid-intlayer";
      
      export const useLocalizedNavigate = () => {
        const navigate = useNavigate();
        const { locale } = useLocale();
      
        const localizedNavigate = (to: string) => {
          const localizedTo = getLocalizedUrl(to, locale());
          return navigate({ to: localizedTo });
        };
      
        return localizedNavigate;
      };
      
    9. Utilizar o Intlayer em Suas Páginas

      Use useIntlayer por padrão: é a forma recomendada de ler conteúdo dentro dos componentes, e o compilador o resolve para a localidade que está sendo renderizada. Recorra a getIntlayer / getIntlayerAsync apenas fora da árvore Solid: o head das rotas, os loaders e as server functions.

      Acesse seus dicionários de conteúdo através da sua aplicação:

      Página Inicial Localizada

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/solid-router";
      import { useIntlayer } from "solid-intlayer";
      import { LocalizedLink } from "@/components/LocalizedLink";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
      });
      
      function RouteComponent() {
        const content = useIntlayer("index-page");
      
        return (
          <main>
            <h1>{content.heroTitle}</h1>
            <p>{content.heroDesc}</p>
            <div>
              <LocalizedLink to="/">{content.navHome}</LocalizedLink>
              <LocalizedLink to="/about">{content.navAbout}</LocalizedLink>
            </div>
          </main>
        );
      }
      
      html
      <img src="{content.image.src.value}" alt="{content.image.value}" />
      <img src="{content.image.src.toString()}" alt="{content.image.toString()}" />
      <img src="{String(content.image.src)}" alt="{String(content.image)}" />
      

      No Solid, useIntlayer retorna conteúdo reativo (por exemplo, content). Você pode acessar suas propriedades diretamente.

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

    10. Criar um Componente Seletor de Localidade

      Crie um componente para permitir que os usuários mudem de língua:

      src/components/LocaleSwitcher.tsx
      import { useLocation } from "@tanstack/solid-router";
      import { getLocaleName, getPathWithoutLocale, getPrefix } from "intlayer";
      import { For } from "solid-js";
      import { useIntlayer, useLocale } from "solid-intlayer";
      import { LocalizedLink, type To } from "./LocalizedLink";
      
      export const LocaleSwitcher = () => {
        const content = useIntlayer("locale-switcher");
        const location = useLocation();
      
        const { availableLocales, locale, setLocale } = useLocale();
      
        const pathWithoutLocale = () => getPathWithoutLocale(location().pathname);
      
        return (
          <div class="flex flex-row gap-2">
            <For each={availableLocales}>
              {(localeEl) => (
                <LocalizedLink
                  aria-current={localeEl === locale() ? "page" : undefined}
                  onClick={() => setLocale(localeEl)}
                  params={{ locale: getPrefix(localeEl).localePrefix }}
                  to={pathWithoutLocale() as To}
                >
                  {getLocaleName(localeEl)}
                </LocalizedLink>
              )}
            </For>
          </div>
        );
      };
      
      export default LocaleSwitcher;
      

      No Solid, locale de useLocale é um signal accessor. Use locale() (com parênteses) para ler o seu valor atual de forma reativa.

      Para aprender mais sobre o hook useLocale, consulte la documentação.

    11. Gerenciamento de Atributos HTML

      Como visto no Passo 5, você pode gerenciar os atributos lang e dir da tag html usando useParams no seu componente raiz. Isso garante que os atributos corretos sejam definidos no servidor e no cliente.

      src/routes/__root.tsx
      const RootComponent: ParentComponent = (props) => {
        const params = localeRoute.useParams();
        const locale = params()?.locale ?? defaultLocale;
      
        return (
          <html dir={getHTMLTextDir(locale)} lang={locale}>
            {/* ... */}
          </html>
        );
      };
      

    12. Adicionar Middleware

      Você também pode usar o intlayerProxy para adicionar roteamento do lado do servidor à sua aplicação. Este plugin detectará automaticamente a localidade atual baseada na URL e definirá o cookie de localidade apropriado. Se nenhuma localidade for especificada, o plugin determinará a localidade mais apropriada baseada nas preferências de língua do navegador do usuário. Se nenhuma localidade for detectada, ele redirecionará para a localidade padrão.

      Note que para usar o intlayerProxy em produção, você precisa trocar o pacote vite-intlayer de devDependencies para dependencies.
      Desde Intlayer v9, intlayerProxy() é agrupado diretamente no plugin intlayer() e ativado por padrão através da opção routing.enableProxy (true por padrão). Registrá-lo separadamente conforme mostrado abaixo agora é opcional: é mantido para compatibilidade com versões anteriores e para configurações que precisam controlar a ordem dos plugins. Defina routing.enableProxy: false para recusar. Consulte as notas de lançamento da v9.
      vite.config.ts
      import { tanstackStart } from "@tanstack/solid-start/plugin/vite";
      import solid from "vite-plugin-solid";
      import { nitro } from "nitro/vite";
      import { defineConfig } from "vite";
      import { intlayer } from "vite-intlayer";
      
      export default defineConfig({
        plugins: [
          nitro(),
          intlayer({
            proxy: {
              ignore: (req) => req.url?.startsWith("/api"),
            },
          }),
          tanstackStart({
            router: {
              routeFileIgnorePattern:
                ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5|md|mdx|yaml|yml)$",
            },
          }),
          solid(),
        ],
      });
      

    13. Internacionalizar Seus Metadados

      getIntlayer resolve de forma síncrona contra o dicionário mesclado, aquele que contém todas as localidades declaradas. O head continua síncrono e nada é aguardado, mas todo o dicionário multilíngue é incluído no chunk da rota enviado ao navegador.

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/solid-router";
      import {
        defaultLocale,
        getIntlayer,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        head: ({ params }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // The path for this route
      
          const metaContent = getIntlayer("app", locale);
      
          return {
            links: [
              // Canonical link: Points to the current localized page
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: Tell Google about all localized versions
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: For users in unmatched languages
              // Define the default fallback locale (usually your primary language)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: metaContent.title },
              { name: "description", content: metaContent.meta.description },
            ],
          };
        },
      });
      

      Ideal para dicionários de metadados pequenos, poucas localidades ou durante a prototipagem.

      getIntlayerAsync (disponível a partir da v9.4) se comporta como getIntlayer, mas o plugin de build o aponta para o chunk por localidade em .intlayer/dynamic_dictionaries/ em vez do dicionário mesclado. Assim, uma página envia apenas a localidade que renderiza. Como esse chunk é carregado sob demanda, o head passa a ser async:

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/solid-router";
      import {
        defaultLocale,
        getIntlayerAsync,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        head: async ({ params }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // The path for this route
      
          const metaContent = await getIntlayerAsync("app", locale);
      
          return {
            links: [
              // Canonical link: Points to the current localized page
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: Tell Google about all localized versions
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: For users in unmatched languages
              // Define the default fallback locale (usually your primary language)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: metaContent.title },
              { name: "description", content: metaContent.meta.description },
            ],
          };
        },
      });
      
      Se um head lê vários dicionários, resolva-os com Promise.all: aguardar cada getIntlayerAsync em sua própria linha encadeia as requisições em vez de executá-las em paralelo.

      O contraponto: o import dinâmico é resolvido enquanto o head executa, no caminho crítico da renderização do documento. Numa rota fria isso atrasa o head em alguns milissegundos e pode degradar levemente o LCP.

      Resolva o dicionário no loader da rota e leia-o de volta a partir de loaderData no head. Os loaders das rotas correspondentes rodam em paralelo, e staleTime: Infinity informa ao TanStack Router que o resultado nunca expira, então o chunk por localidade é resolvido uma única vez e depois servido do cache do router, mantendo o head síncrono.

      src/routes/{-$locale}/index.tsx
      import { createFileRoute } from "@tanstack/solid-router";
      import {
        defaultLocale,
        getIntlayerAsync,
        getLocalizedUrl,
        localeMap,
      } from "intlayer";
      
      export const Route = createFileRoute("/{-$locale}/")({
        component: RouteComponent,
        // Resolved in parallel with the other matched routes, off the head critical path
        loader: async ({ params }) => {
          const { locale = defaultLocale } = params;
      
          return { metaContent: await getIntlayerAsync("app", locale) };
        },
        // The dictionary never changes for a given locale: resolve the chunk once
        staleTime: Infinity,
        head: ({ params, loaderData }) => {
          const { locale = defaultLocale } = params;
          const path = "/"; // The path for this route
      
          return {
            links: [
              // Canonical link: Points to the current localized page
              { rel: "canonical", href: getLocalizedUrl(path, locale) },
      
              // Hreflang: Tell Google about all localized versions
              ...localeMap(({ locale: mapLocale }) => ({
                rel: "alternate",
                hrefLang: mapLocale,
                href: getLocalizedUrl(path, mapLocale),
              })),
      
              // x-default: For users in unmatched languages
              // Define the default fallback locale (usually your primary language)
              {
                rel: "alternate",
                hrefLang: "x-default",
                href: getLocalizedUrl(path, defaultLocale),
              },
            ],
            meta: [
              { title: loaderData?.metaContent.title },
              {
                name: "description",
                content: loaderData?.metaContent.meta.description,
              },
            ],
          };
        },
      });
      
      O head pode ser chamado antes de o loader resolver, então loaderData é tipado como possivelmente undefined. Mantenha o encadeamento opcional ou retorne um título de fallback.

      Você mantém o chunk por localidade sem pagar seu custo no caminho crítico do head. O preço é a experiência de desenvolvimento: o conteúdo precisa ser repassado explicitamente do loader para o head via loaderData.

      Qual resolução escolher?

      Resolução estática Resolução dinâmica Resolução dinâmica em cache
      API getIntlayer getIntlayerAsync (v9.4+) getIntlayerAsync no loader (v9.4+)
      Assinatura do head síncrona async síncrona, lê loaderData
      Localidades enviadas todas as localidades declaradas apenas a localidade solicitada apenas a localidade solicitada
      Navegações no cliente nada a resolver reexecutado a cada correspondência servido do cache do router
      Experiência de desenvolvimento a mais simples um único await conteúdo repassado via loaderData

    14. Recuperar a localidade nas suas server actions

      Você pode querer acessar a localidade atual de dentro das suas server actions ou endpoints de API. Você pode fazer isso usando o helper getLocale do intlayer.

      Aqui está um exemplo usando as server functions do TanStack Start:

      src/routes/{-$locale}/index.tsx
      import { createServerFn } from "@tanstack/solid-start";
      import {
        getRequestHeader,
        getRequestHeaders,
      } from "@tanstack/solid-start/server";
      import { getCookie, getIntlayer, getLocale } from "intlayer";
      
      export const getLocaleServer = createServerFn().handler(async () => {
        const locale = await getLocale({
          // Obter o cookie da requisição (padrão: 'INTLAYER_LOCALE')
          getCookie: (name) => {
            const cookieString = getRequestHeader("cookie");
      
            return getCookie(name, cookieString);
          },
          // Obter o header da requisição (padrão: 'x-intlayer-locale')
          // Fallback usando negociação Accept-Language
          getHeader: (name) => getRequestHeader(name),
        });
      
        // Recuperar algum conteúdo usando getIntlayer()
        const content = getIntlayer("app", locale);
      
        return { locale, content };
      });
      

    15. Gerenciar páginas não encontradas

      Quando um usuário visita uma página inexistente, você pode exibir uma página personalizada de não encontrado e o prefixo de localidade pode impactar a maneira como a página de não encontrado é disparada.

      Entendendo o Processamento de 404 do TanStack Router com Prefixos de Localidade

      No TanStack Router, processar páginas 404 com rotas localizadas requer uma abordagem multicamada:

      1. Rota de 404 dedicada: Uma rota específica para exibir a UI do 404
      2. Validação ao nível da rota: Valida os prefixos de localidade e redireciona os inválidos para o 404
      3. Rota catch-all: Captura quaisquer caminhos não correspondentes dentro do segmento de localidade
      src/routes/{-$locale}/404.tsx
      import { createFileRoute } from "@tanstack/solid-router";
      
      // Isso cria uma rota /[locale]/404 dedicada
      // Ela é usada tanto como uma rota direta quanto importada como um componente em outros arquivos
      export const Route = createFileRoute("/{-$locale}/404")({
        component: NotFoundComponent,
      });
      
      // Exportado separadamente para que possa ser reutilizado em notFoundComponent e rotas catch-all
      export function NotFoundComponent() {
        return (
          <div>
            <h1>404</h1>
          </div>
        );
      }
      
      src/routes/{-$locale}/route.tsx
      import { createFileRoute, Outlet, redirect } from "@tanstack/solid-router";
      import { validatePrefix } from "intlayer";
      import { NotFoundComponent } from "./404";
      
      export const Route = createFileRoute("/{-$locale}")({
        // beforeLoad executa antes da rota renderizar (tanto no servidor quanto no cliente)
        // É o lugar ideal para validar o prefixo de localidade
        beforeLoad: ({ params }) => {
          const localeParam = params.locale;
      
          // validatePrefix verifica se a localidade é válida de acordo com a sua configuração do intlayer
          const { isValid, localePrefix } = validatePrefix(localeParam);
      
          if (!isValid) {
            // Prefixo de localidade inválido - redireciona para a página 404 com um prefixo de localidade válido
            throw redirect({
              to: "/{-$locale}/404",
              params: { locale: localePrefix },
            });
          }
        },
        component: Outlet,
        // notFoundComponent é chamado quando uma rota filha não existe
        // ex: /en/pagina-nao-existente dispara isso dentro do layout /en
        notFoundComponent: NotFoundComponent,
      });
      
      src/routes/{-$locale}/$.tsx
      import { createFileRoute } from "@tanstack/solid-router";
      
      import { NotFoundComponent } from "./404";
      
      // A rota $ (splat/catch-all) corresponde a qualquer caminho que não corresponda a outras rotas
      // ex: /en/algum/caminho/profundamente/aninhado/invalido
      // Isso garante que TODOS os caminhos não correspondentes dentro de uma localidade mostrem a página 404
      // Sem isso, caminhos profundos não correspondentes poderiam mostrar uma página em branco ou erro
      export const Route = createFileRoute("/{-$locale}/$")({
        component: NotFoundComponent,
      });
      
    16. Extrair o conteúdo dos seus componentes

      Opcional

      Se você tem uma base de código existente, transformar milhares de arquivos pode ser demorado.

      Para facilitar este processo, o Intlayer propõe um compilador / extrator para transformar os seus componentes e extrair o conteúdo.

      Para configurá-lo, você pode adicionar uma seção compiler no seu arquivo intlayer.config.ts:

      intlayer.config.ts
      import { type IntlayerConfig } from "intlayer";
      
      const config: IntlayerConfig = {
        // ... Resto da sua configuração
        compiler: {
          /**
           * Indica se o compilador deve ser habilitado.
           */
          enabled: true,
      
          /**
           * Define o caminho dos arquivos de saída
           */
          output: ({ fileName, extension }) => `./${fileName}${extension}`,
      
          /**
           * Indica se os componentes devem ser salvos após serem transformados.
           *
           * - Se `true`, o compilador reescreverá o arquivo do componente no disco. Assim, a transformação será permanente, e o compilador pulará a transformação para o próximo processo. Dessa forma, o compilador pode transformar a aplicação e depois pode ser removido.
           *
           * - Se `false`, o compilador injetará a chamada de função `useIntlayer()` no código apenas no output da build, mantendo a base de código intacta. A transformação será feita apenas em memória.
           */
          saveComponents: false,
      
          /**
           * Prefixo da chave do dicionário
           */
          dictionaryKeyPrefix: "",
        },
      };
      
      export default config;
      

      Rode o extrator para transformar os seus componentes e extrair o conteúdo

      bash
      npx intlayer extract
      
      Since v9, the intlayerCompiler is included in the intlayer plugin. So you don't need to add it manually.

      Atualize o seu vite.config.ts para incluir o plugin intlayerCompiler:

      vite.config.ts
      import { intlayer, intlayerCompiler } from "vite-intlayer";
      import { defineConfig } from "vite";
      import { devtools } from "@tanstack/devtools-vite";
      import { tanstackStart } from "@tanstack/solid-start/plugin/vite";
      import solidPlugin from "vite-plugin-solid";
      
      export default defineConfig({
        plugins: [
          devtools(),
          tanstackStart({
            router: {
              routeFileIgnorePattern:
                ".content.(ts|tsx|js|mjs|cjs|jsx|json|jsonc|json5|md|mdx|yaml|yml)$",
            },
          }),
          solidPlugin({ ssr: true }),
          intlayer(),
          intlayerCompiler(), // Adds the compiler plugin
        ],
      });
      
      bash
      npm run build # Ou rpm run dev
      

    17. Gerar um Sitemap

      O Intlayer vem com um gerador de sitemap integrado para ajudá-lo a criar facilmente um sitemap para sua aplicação. Ele cuida das rotas localizadas e adiciona os metadados necessários para os mecanismos de busca.

      O sitemap gerado pelo Intlayer suporta o namespace xhtml:link (Hreflang XML Extensions). Ao contrário dos geradores de sitemap padrão que apenas listam URLs brutos, o Intlayer cria automaticamente os links bidirecionais necessários entre todas as versões de idioma de uma página (por exemplo, /about, /about?lang=fr e /about?lang=es). Isso garante que os mecanismos de busca indexem e sirvam corretamente a versão de idioma certa para o público certo.

      Para usá-lo, você primeiro precisa configurar o seu vite.config.ts para habilitar a pré-renderização de suas rotas localizadas e desabilitar a geração de sitemap padrão do TanStack Start.

      vite.config.ts
      import { localeMap, localeFlatMap } from "intlayer";
      // ... outras importações
      
      export const pathList = ["", "/about", "/404"];
      
      const localizedPages = localeFlatMap(({ urlPrefix }) =>
        pathList.map((path) => ({
          path: `${urlPrefix}${path}`,
          prerender: {
            enabled: true,
          },
        }))
      );
      
      export default defineConfig({
        plugins: [
          // ... outros plugins
          tanstackStart({
            // ... outras configurações
            sitemap: {
              enabled: false,
            },
            prerender: {
              enabled: true,
              crawlLinks: false,
              concurrency: 10,
            },
            pages: localizedPages,
          }),
        ],
      });
      

      Em seguida, crie uma rota src/routes/sitemap[.]xml.ts que use a função generateSitemap:

      src/routes/sitemap[.]xml.ts
      import { createFileRoute } from "@tanstack/solid-router";
      import { generateSitemap } from "intlayer";
      
      const SITE_URL = "http://localhost:3000";
      
      export const Route = createFileRoute("/sitemap.xml")({
        server: {
          handlers: {
            GET: async () => {
              const sitemap = generateSitemap(
                [
                  { path: "/", changefreq: "daily", priority: 1.0 },
                  { path: "/about", changefreq: "monthly", priority: 0.8 },
                ],
                { siteUrl: SITE_URL }
              );
      
              return new Response(sitemap, {
                headers: { "Content-Type": "application/xml" },
              });
            },
          },
        },
      });
      

    18. Configurar TypeScript

      O Intlayer usa a ampliação de módulo para obter os benefícios do TypeScript e tornar a sua base de código mais forte.

      Certifique-se de que a sua configuração do TypeScript inclua os tipos autogerados:

      tsconfig.json
      {
        // ... as suas configurações existentes
        include: [
          // ... os seus includes existentes
          ".intlayer/**/*.ts", // Incluir os tipos autogerados
        ],
      }
      

    Configuração Git

    É recomendado ignorar os arquivos gerados pelo Intlayer. Isso permite que você evite committá-los no seu repositório Git.

    Para fazer isso, você pode adicionar as seguintes instruções ao seu arquivo .gitignore:

    .gitignore
    # Ignorar os arquivos gerados pelo Intlayer
    .intlayer
    

    Extensão VS Code

    Para melhorar a sua experiência de desenvolvimento com o Intlayer, você pode instalar a Extensão oficial do Intlayer VS Code.

    Instalar a partir do VS Code Marketplace

    Esta extensão oferece:

    • Autocompletar para chaves de tradução.
    • Detecção de erros em tempo real para traduções ausentes.
    • Previsões inline de conteúdo traduzido.
    • Ações rápidas para criar e atualizar traduções facilmente.

    Para mais detalhes sobre como usar a extensão, consulte a documentação da Extensão Intlayer VS Code.


    Ir Além

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


    Referências de Documentação