Autor:
    Criação:2026-04-20Última atualização:2026-05-18

    Bibliotecas i18n para Svelte - Relatório de Benchmark 2026

    Esta página é um relatório de benchmark para soluções i18n no Svelte.

    Índice

    Benchmark Interativo

    Referência de resultados:

    intlayer.org
    Ver dados completos do benchmark

    Veja o repositório completo do benchmark aqui.

    Introdução

    As soluções de internacionalização estão entre as dependências mais pesadas em um app Svelte. O principal risco é enviar conteúdo desnecessário: traduções para outras páginas e outros locais no bundle de uma única rota.

    À medida que seu app cresce, esse problema pode rapidamente explodir o JavaScript enviado ao cliente e tornar a navegação lenta.

    Na prática, para as implementações menos otimizadas, uma página internacionalizada pode acabar sendo várias vezes mais pesada do que a versão sem i18n.

    O outro impacto é na experiência do desenvolvedor (DX): como você declara conteúdo, tipos, organização de namespaces, carregamento dinâmico e reatividade quando o local muda.

    TL;DR

    • Intlayer: A escolha mais eficiente em desempenho (v8.7.12) com o menor footprint.
    • Paraglide: Forte candidato para tree-shaking, mas possui uma experiência de desenvolvedor mais complexa e overhead de reatividade.
    • svelte-i18n: Abrangente e padrão para Svelte, mas carrega um peso de bundle muito maior (~7× o Intlayer).

    Teste seu app

    Para identificar rapidamente problemas de vazamento de i18n, configurei um scanner gratuito disponível aqui.

    intlayer.org

    O problema

    Duas alavancas são essenciais para limitar o custo de um app multilíngue:

    • Dividir o conteúdo por página / namespace para no carregar dicionários inteiros quando não for necessário.
    • Carregar o local correto dinamicamente, apenas quando necessário.

    Entendendo as limitações técnicas dessas abordagens:

    Carregamento dinâmico

    Sem carregamento dinâmico, a maioria das soluções mantém as mensagens na memória desde o primeiro render, o que adiciona um overhead significativo para apps com muitas rotas e locais.

    Com carregamento dinâmico, você aceita uma troca: menos JS inicial, mas às vezes uma requisição extra ao trocar de idioma.

    Divisão de conteúdo (Splitting)

    Sintaxes construídas em torno de t('a.b.c') são muito convenientes, mas frequentemente incentivam a manutenção de grandes objetos JSON em tempo de execução. Esse modelo torna o tree-shaking difícil, a menos que a biblioteca ofereça uma estratégia real de divisão por página.

    Metodologia

    Para este benchmark, comparamos as seguintes bibliotecas:

    • Base App (Sem biblioteca i18n)
    • svelte-intlayer (v8.7.12)
    • svelte-i18n (v4.0.1)
    • @inlang/paraglide-js (v2.17.0)

    O framework é Svelte com um app multilíngue de 10 páginas e 10 idiomas.

    Comparamos quatro estratégias de carregamento:

    Estratégia Sem namespaces (global) Com namespaces (scoped)
    Carregamento estático Static: Tudo na memória ao iniciar. Scoped static: Dividido por namespace; tudo carregado ao iniciar.
    Carregamento dinâmico Dynamic: Carregamento sob demanda por local. Scoped dynamic: Carregamento granular por namespace e local.

    Resumo das estratégias

    • Static: Simples; sem latência de rede após o carregamento inicial. Desvantagem: grande tamanho de bundle.
    • Dynamic: Reduz o peso inicial (lazy-loading). Ideal quando você tem muitos locais.
    • Scoped static: Mantém o código organizado (separação lógica) sem requisições de rede extras complexas.
    • Scoped dynamic: Melhor abordagem para code splitting e desempenho. Minimiza a memória carregando apenas o que a view atual e o local ativo precisam.

    O que medi:

    Executei o mesmo aplicativo multilíngue em um navegador real para cada stack, depois anotei o que realmente apareceu na rede e quanto tempo levou. Os tamanhos são reportados após a compressão web normal, porque isso é mais próximo do que as pessoas baixam do que contagens de código-fonte bruto.

    • Tamanho da biblioteca de internacionalização: Após bundling, tree-shaking e minificação, o tamanho da biblioteca i18n é o tamanho do código de providers + stores em um componente vazio. Não inclui o carregamento de arquivos de tradução. Responde o quanto a biblioteca custa antes do seu conteúdo entrar em cena.

    • JavaScript por página: Para cada rota de benchmark, quanto script o navegador puxa nessa visita, em média nas páginas do conjunto (e entre locales onde o relatório as agrupa). Páginas pesadas são páginas lentas.

    • Vazamento de outros locales: É o conteúdo da mesma página mas em outro idioma que seria carregado por engano na página auditada. Este conteúdo é desnecessário e deve ser evitado. (ex: conteúdo da página /fr/about no bundle da página /en/about)

    • Vazamento de outras rotas: A mesma ideia para outras telas do app: se a sua cópia vem junto quando você apenas abriu uma página. (ex: conteúdo da página /en/about no bundle da página /en/contact). Uma pontuação alta sugere splitting fraco ou bundles muito amplos.

    • Tamanho médio do bundle do componente: Peças comuns da UI são medidas uma de cada vez em vez de se esconder dentro de um número gigante do app. Mostra se a internacionalização silenciosamente inflaciona componentes cotidianos. Por exemplo, se seu componente fizer rerender, carregará todos esses dados da memória. Anexar um JSON gigante a qualquer componente é como conectar um grande armazenamento de dados não utilizados que desacelerará o desempenho de seus componentes.

    • Responsividade da troca de idioma: Eu troco o idioma usando o controle do próprio app e cronometro quanto tempo leva até a página ter claramente mudado, o que um visitante notaria, não um micro-passo de laboratório.

    • Trabalho de renderização após uma troca de idioma: Um follow-up mais estreito: quanto esforço a interface levou para repintar para o novo idioma uma vez que a troca está em andamento. Útil quando o tempo "sentido" e o custo do framework divergem.

    • Tempo de carregamento inicial da página: Da navegação até o navegador considerar a página totalmente carregada para os cenários que testei. Bom para comparar cold starts.

    • Tempo de hidratação: Quando o app o expõe, quanto tempo o cliente gasta transformando HTML do servidor em algo que você realmente pode clicar. Um travessão nas tabelas significa que essa implementação não forneceu um número de hidratação confiável neste benchmark.

    Estrelas do GitHub

    As estrelas do GitHub são um forte indicador da popularidade de um projeto, da confiança da comunidade e da relevância a longo prazo. Embora não sejam uma medida direta da qualidade técnica, elas refletem quantos desenvolvedores consideram o projeto útil, acompanham seu progresso e têm probabilidade de adotá-lo. Para estimar o valor de um projeto, as estrelas ajudam a comparar a tração entre as alternativas e fornecem insights sobre o crescimento do ecossistema.

    Star History Chart

    Resultados em detalhes

    1 - Soluções a evitar

    Nenhuma solução clara a evitar no ecossistema Svelte.

    2 - Soluções aceitáveis

    (Paraglide) (@inlang/paraglide-js@2.17.0):

    O Paraglide oferece uma abordagem inovadora e bem pensada. No contexto de um app Vite + Svelte, o tree-shaking que a empresa anuncia funcionou conforme o esperado, o que é excelente. Mas no caso do React + TanStack Start, o tree-shaking não funcionou como esperado, o mesmo para o Next.js. Dito isso, o uso do Paraglide em um projeto Svelte e TanStack Start valeria uma dupla verificação. O fluxo de trabalho e a DX também são mais complexos do que outras opções. Pessoalmente, não gosto de ter que regenerar arquivos JS antes de cada push, o que cria um risco constante de conflito de merge através de PRs. A ferramenta também parece mais focada no Vite do que no Next.js. Finalmente, em comparação com outras soluções, o Paraglide não usa um store (ex: Svelte store) para recuperar o local atual para renderizar o conteúdo. Para cada nó analisado, ele solicitará o local do localStorage / cookie etc. Isso leva à execução de lógica desnecessária que impacta a reatividade do componente.

    Nota sobre o paraglide: a solução injeta código em sua base de código para importações; como resultado, a métrica 'lib size' no relatório de benchmark é quase 0. A geração de código é algo bom, porque a função utilizada incluirá apenas a lógica necessária (prefixo em todos os lugares vs sem prefixo, cookie vs armazenamento, etc.). Em comparação, o Intlayer realiza essa filtragem via injeções de variáveis de ambiente durante o build para forçar o bundler a fazer tree-shaking do conteúdo dependendo da lógica. Graças a isso, o paraglide e o intlayer acabam sendo soluções de 6 a 10 vezes mais leves que o i18next ou o next-intl.

    (svelte-i18n) (svelte-i18n@3.4.0):

    Esta solução atende a todas as necessidades de i18n em um projeto Svelte. Mas, como é o caso do i18next ou de outras grandes soluções de i18n, é um pouco pesada (~15.9kb, o que é cerca de 7× o svelte-intlayer).

    3 - Recomendações

    (Intlayer) (svelte-intlayer@8.7.12):

    Eu não julgarei pessoalmente o svelte-intlayer por uma questão de objetividade, já que é minha própria solução.

    Nota pessoal

    Esta nota é pessoal e não afeta os resultados do benchmark. Ainda assim, no mundo do i18n, você costuma ver consenso em torno de um padrão como const t = useTranslation('xx') + <>{t('xx.xx')}</> para o conteúdo traduzido.

    Em apps Svelte, injetar uma função como um Slot é, na minha opinião, um antipadrão. Também adiciona complexidade evitável e overhead de execução de JavaScript (mesmo que seja quase imperceptível).