Autor:
    Criação:2026-09-13Última atualização:2026-09-22

    vue-i18n VS @intlayer/vue-i18n: Mesma API, Bundle Diferente

    @intlayer/vue-i18n é um adaptador de compatibilidade: expõe a API vue-i18n (createI18n, useI18n, t(), d(), n(), $t, v-t, i18n.global.locale...) e a serve a partir de dicionários compilados pelo Intlayer. Seus arquivos .vue não mudam. O que t("footer.github") está vinculado é que muda.

    Este artigo mede essa troca na mesma aplicação Vite + Vue 3, construída uma vez com vue-i18n e outra com o adapter. Os números vêm de Benchmark Bloom. Para vue-i18n e Intlayer comparados como bibliotecas, leia vue-i18n vs Intlayer e o benchmark vue-i18n vs Intlayer. Este é sobre o que o adapter muda quando você mantém seus componentes como estão.

    tl;dr: Na mesma aplicação Vite + Vue 3, substituir vue-i18n por @intlayer/vue-i18n reduziu o JavaScript por página de 134.9 KB para 47.0 KB gzip (a app sem i18n pesa 41.3 KB), o runtime de 24.3 KB para 7.9 KB, o componente médio de 196 KB para 8.4 KB, e o vazamento de strings de páginas estrangeiras de 90% para 0%, sem editar nenhum arquivo .vue. createI18n({ messages }) continua funcionando como fallback; remova as importações JSON para obter os números acima. Blocos SFC <i18n> e setLocaleMessage() em runtime são as duas features que não são transportadas.

    O que é @intlayer/vue-i18n

    vue-i18n é um runtime. createI18n({ messages: { en, fr, ... } }) constrói uma instância global contendo todas as mensagens de todos os locales; useI18n() vincula cada componente a ela; t("footer.github") percorre a árvore em tempo de renderização. Esse design é o que torna os blocos SFC <i18n> e setLocaleMessage() possíveis, e é também por isso que o gráfico de dependências de cada componente inclui a árvore inteira.

    @intlayer/vue-i18n mantém a API e substitui a árvore:

    1. Import aliasing. vueI18nVitePlugin() de @intlayer/vue-i18n/plugin envolve vite-intlayer e adiciona um resolve.alias para que vue-i18n seja resolvido para @intlayer/vue-i18n. Nenhuma importação é renomeada.
    2. JSON como fonte da verdade. O plugin syncJSON lê seu locales/{locale}.json existente com format: "vue-i18n" (para que {name}, {0} interpolação de lista e "car | cars" plurais com pipe sejam analisados corretamente) e escreve traduções de volta quando a CLI ou o CMS as atualiza.
    3. Call-site binding. A passagem de otimização do Intlayer reescreve os locais de chamada useI18n() para que o componente receba os dicionários nomeados por suas chaves, na locale ativa, como imports que o bundler pode rastrear e dividir.
    src/components/Footer.vue
    <!-- Seu código, inalterado -->
    <script setup lang="ts">
    import { useI18n } from "vue-i18n";
    
    const { t } = useI18n();
    </script>
    
    <template>
      <a href="https://github.com/intlayer-org/benchmark-bloom">{{
        t("footer.github")
      }}</a>
    </template>
    
    O que o compilador emite (simplificado)
    import _dicHash_footer from "../.intlayer/dictionaries/footer.mjs";
    import { useDictionary as useI18n } from "@intlayer/vue-i18n";
    
    const { t } = useI18n(_dicHash_footer);
    

    O componente não alcança mais a árvore global de mensagens. Ele alcança footer. É por isso que a coluna de tamanho do componente abaixo cai de 196 KB para 8 KB.

    O que o adaptador mantém, ignora e não substitui

    vue-i18n APICom @intlayer/vue-i18n
    useI18n() → { t, d, n, te, tm, rt, locale, availableLocales }✅ Mantido. As chaves de t são digitadas em relação aos seus dicionários
    t("key", { name }), t("key", [a, b]), t("key", count)✅ Mantido. {name}, {0} e plurais separados por pipe são resolvidos como antes
    d(date, "long"), n(value, "currency")✅ Mantido. datetimeFormats / numberFormats de createI18n() são respeitados, apoiados pelo Intl nativo
    i18n.global.locale.value = "fr"✅ Mantido. Uma WritableComputedRef apoiada pelo cliente Intlayer; a reatividade funciona como antes
    $t, $tc, $te, $tm, $rt, $d, $n, $i18n (Options API)✅ Mantido. Registrado em app.config.globalProperties por app.use(i18n)
    v-t directive✅ Mantido
    legacy: true✅ Aceito
    createI18n({ messages })⚠️ messages são usados como um fallback em runtime com um aviso de dev. Remova as importações JSON para ganhos no bundle
    setLocaleMessage(), mergeLocaleMessage()❌ Aviso e sem ação. O carregamento de mensagens em runtime é substituído por dicionários em build-time
    SFC <i18n> custom blocks❌ Não lido. Mova essas mensagens para o JSON de locale (ou um .content.ts próximo ao componente)
    @nuxtjs/i18n⚠️ Adapter separado, veja a documentação de compatibilidade Nuxt

    O benchmark

    O que foi medido

    O Benchmark Bloom suite constrói a mesma aplicação Vite + Vue 3 com cada setup: 10 páginas (home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10 locales (en, fr, es, de, it, pt, zh, ja, ko, ru), componentes idênticos e conteúdo idêntico. As páginas são medidas em en e fr.

    Ambas foram construídas na configuração static, a que a maioria dos projetos Vue faz deploy: para vue-i18n, todo o JSON de cada locale é importado e passado para createI18n({ messages }); para o adapter, os mesmos componentes com vite.config.ts e intlayer.config.ts alterados e a importação de messages removida. O vue-intlayer nativo é incluído como referência.

    Para cada build, a suite registra:

    • Tamanho da lib: tamanho gzip (e minificado) de um componente vazio que apenas importa a biblioteca i18n.
    • Page JS: JavaScript gzip baixado por página, média de todas as páginas e locales.
    • Locale leak %: compartilhamento de strings traduzidas no JS baixado que pertencem a um locale que o usuário não está visualizando.
    • Page leak %: compartilhamento de strings traduzidas no JS baixado que pertencem a uma página em que o usuário não está.
    • Component avg: tamanho gzip médio de cada componente compilado isoladamente.
    • E2E reactivity: tempo decorrido entre selecionar uma nova locale e html[lang] ser atualizado no DOM (Playwright, 5 iterações).
    • Page load: PerformanceNavigationTiming.duration.
    Os números abaixo vêm da execução datada de 2026-09-12 com vue-i18n 11.4.0 e @intlayer/vue-i18n 9.5.1. A aplicação de teste é deliberadamente pequena (algumas dezenas de strings por locale), então as porcentagens de vazamento descrevem um padrão: elas crescem com seu conteúdo enquanto o custo de runtime permanece fixo.

    Resultados em Vite + Vue 3

    Escolha as métricas e as bibliotecas que importam para você:

    Métrica

    Carregamento JSON dinâmico

    Carrega as traduções tardiamente em tempo de execução

    JSON com escopo (namespacing)

    Namespaces de tradução por página

    O que é essa métrica?

    O tamanho total compactado em gzip do pacote da biblioteca de internacionalização. Inclui apenas o provedor e a lógica de recuperação de conteúdo após o tree-shaking e a minificação.

    Por que é importante?

    Um tamanho de biblioteca menor reduz a carga útil inicial de JavaScript, resultando em tempos de download e execução mais rápidos no cliente.

    Ver como

    SetupStrategyLib size (gz)Lib size (min)Page JS avg (gz)Locale leakPage leakComponent avg (gz)E2E reactivityPage load
    base (sem i18n)-0.0 KB0.0 KB41.3 KB0.0%-1.1 KB1.8 ms10.8 ms
    vue-i18nstatic24.3 KB83.2 KB134.9 KB50.0%90.0%196.0 KB2.8 ms13.6 ms
    @intlayer/vue-i18nstatic7.9 KB23.2 KB47.0 KB15.0%0.0%8.4 KB1.5 ms9.3 ms
    vue-intlayer (nativo)static3.9 KB11.1 KB57.1 KB56.8%0.0%7.7 KB4.5 ms13.8 ms
    vue-intlayer (native)dynamic3.9 KB11.1 KB59.8 KB50.0%0.0%6.5 KB4.0 ms15.8 ms
    A coluna page-leak da aplicação base é deixada em branco: sem biblioteca i18n, a coleta de impressões digitais detecta strings codificadas em chunks compartilhados e o número não é significativo.

    Como ler

    • 88 KB a menos por página, mesmos componentes. vue-i18n leva a aplicação de 41.3 KB para 134.9 KB. O build do adaptador dos mesmos componentes chega a 47.0 KB, 5.7 KB acima da aplicação base. A maior parte da diferença é os 74.9 KB de src/locales que createI18n({ messages }) puxa para cada página e o adaptador nunca agrupa como um bloco.
    • O runtime encolhe 3x. Um componente vazio que apenas importa vue-i18n custa 24.3 KB gzip / 83.2 KB minified: @intlify/core-base, o compilador de mensagens e o runtime. O adapter custa 7.9 KB / 23.2 KB, a maior parte sendo o core do Intlayer mais a superfície da API vue-i18n.
    • Componentes: 23x menores. Um componente useI18n() compilado em isolamento tem uma média de 196 KB, porque t está vinculado à instância que contém todas as mensagens de cada locale. Com o adapter, o mesmo componente tem uma média de 8.4 KB: ele alcança seu próprio dicionário.
    • Vazamento. vue-i18n fornece cada locale e as strings de cada página em cada página: 50% de vazamento de locale (nas duas locales com fingerprint; com dez locales agrupadas o desperdício real é maior), 90% de vazamento de página. O adapter reduz o vazamento de página para 0% porque cada componente importa apenas seus dicionários. O vazamento de locale fica em 15% nesta execução static; importMode: 'dynamic' é a configuração que o remove, e essa configuração não fazia parte desta execução do Vue.
    • Reatividade e carregamento de página. A troca de locale é barata para ambas (1.5-2.8 ms); o sistema de reatividade do Vue torna isso possível uma vez que as mensagens estão na memória. O carregamento de página vai de 13.6 ms para 9.3 ms, alinhado com 88 KB a menos de JavaScript para fazer parse.
    • Sobre as linhas nativas. vue-intlayer nesta execução agrupou cada locale em modo static e chegou a 57.1 KB com um runtime de 3.9 KB; os dicionários sincronizados do adaptador carregavam menos strings de locales estrangeiros, daí a figura mais baixa por página. O runtime nativo permanece o mais leve dos três, e seu modelo .content.ts é onde os blocos SFC <i18n> encontram seu equivalente.
    Tabela completa, cada biblioteca e cada estratégia, no relatório de benchmark do Vue.

    Por que os números se movem

    The Intlayer compiler extracts content from components

    Nada em src/components/ mudou, então os ganhos vêm do que useI18n está vinculado.

    Com vue-i18n, a ligação é a instância global. createI18n({ messages: { en, fr, ... } }) é uma importação que contém tudo; cada componente que chama useI18n() pode acessar tudo isso, então o bundler não consegue dividir abaixo da instância. Otimizar significa que você divide en.json por rota, chama setLocaleMessage() em um router guard, e mantém o mapa rota-para-arquivo correto conforme os componentes se movem. O desperdício cresce em dois eixos simultaneamente, páginas e idiomas:

    Theoretical content leakage by architecture

    bash
    .
    ├── locales
    │   ├── en.json                    # strings de todas as páginas
    │   └── fr.json
    └── src
        ├── i18n.ts                    # createI18n({ messages: { en, fr, ... } })
        ├── main.ts                    # app.use(i18n)
        └── components
            └── Footer.vue             # useI18n(); t("footer.github")
    

    Com @intlayer/vue-i18n, a binding é o dicionário. syncJSON transforma cada chave de nível superior de en.json em um dicionário; a passagem de otimização entrega ao componente os que seus nomes de chaves, importando o rastreamento do bundler e dividindo por página.

    bash
    .
    ├── intlayer.config.ts             # syncJSON({ format: "vue-i18n", source: ... })
    ├── locales
    │   ├── en.json                    # inalterado, ainda a fonte da verdade
    │   └── fr.json
    ├── .intlayer/                     # gerado: um dicionário por chave de nível superior, por locale
    └── src
        ├── i18n.ts                    # createI18n({})   ← importação de messages removida
        ├── main.ts                    # app.use(i18n)    ← inalterado
        └── components
            └── Footer.vue             # useI18n(); t("footer.github")  ← inalterado
    

    A importação messages em i18n.ts é a única linha a deletar. Isso são 88 KB.

    Migração em três passos

    1. Instalar

      bash
      npx intlayer init --interactive
      

      O comando detecta vue-i18n, instala intlayer, vue-intlayer, @intlayer/vue-i18n e @intlayer/sync-json-plugin, e preenche previamente intlayer.config.ts. Mantenha vue-i18n instalado: é uma peer dependency e fornece os tipos.

    2. Aponte Intlayer para seus arquivos de localização

      intlayer.config.ts
      import { Locales, type IntlayerConfig } from "intlayer";
      import { syncJSON } from "@intlayer/sync-json-plugin";
      
      const config: IntlayerConfig = {
        internationalization: {
          locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],
          defaultLocale: Locales.ENGLISH,
        },
        dictionary: {
          // "static" agrupa todas as locales; "dynamic" carrega a ativa sob demanda
          importMode: "dynamic",
          format: "vue-i18n",
        },
        plugins: [
          syncJSON({
            // dialeto vue-i18n: {name}, {0}, "car | cars"
            format: "vue-i18n",
            source: ({ locale }) => `./locales/${locale}.json`,
            location: "locales",
          }),
        ],
      };
      
      export default config;
      

      locales/{locale}.json permanece onde está. Cada chave de nível superior (footer, hero...) torna-se um dicionário.

    3. Adicionar o plugin e remover a importação de mensagens

      vite.config.ts
      import { defineConfig } from "vite";
      import vue from "@vitejs/plugin-vue";
      import { vueI18nVitePlugin } from "@intlayer/vue-i18n/plugin";
      
      export default defineConfig({
        plugins: [vue(), vueI18nVitePlugin()],
      });
      
      src/i18n.ts
      import { createI18n } from "vue-i18n";
      
      // Antes: createI18n({ locale: "en", messages: { en, fr, es } })
      export const i18n = createI18n({ locale: "en" });
      

      vueI18nVitePlugin() encapsula vite-intlayer (observação de conteúdo, compilação de dicionário, a passagem de otimização) e cria um alias de vue-i18n para o adaptador. Remover a importação de messages é o que reduz os 88 KB; deixá-la ativa mantém a aplicação funcionando mas envia ambos.

    O que você pode deletar depois

    Arquivo / padrãoPor quê
    import en from "./locales/en.json" e similaresUsado apenas como fallback pelo adapter. É aqui que estava os 88 KB
    setLocaleMessage() em route guardsSem efeito. O carregamento por rota agora é responsabilidade do compiler
    @intlify/unplugin-vue-i18nNão necessário: pré-compila mensagens e blocos SFC que o adapter não lê
    Blocos SFC <i18n>Não são lidos; mova-os para o JSON de locale ou para um .content.ts por componente

    O que você ganha além de bytes

    • Chaves tipadas. t("footer.github") é tipado contra o dicionário footer compilado; um caminho incorreto é um erro de TypeScript em vez da chave renderizada como texto.
    • npx intlayer test falha no CI se houver uma chave faltando em qualquer locale. npx intlayer fill traduz as chaves faltando com sua chave de provider (OpenAI, Anthropic, Mistral, Gemini...) e as escreve de volta em locales/{locale}.json.
    • Visual Editor e CMS operam no mesmo JSON, então desenvolvedores não profissionais editam através de uma UI e os arquivos são atualizados.
    • Migração incremental para .content.ts. Qualquer componente pode mudar de useI18n() para useIntlayer("footer") com um arquivo de conteúdo co-localizado. Dicionários JSON e .content.ts coexistem e fazem merge.

    Limites a conhecer antes de começar

    Se suas mensagens estão dentro dos componentes, elas precisam ser movidas para os arquivos de idioma ou para um arquivo .content.ts, que é a mesma ideia com tipos gerados.

    setLocaleMessage() e mergeLocaleMessage() exibem um aviso e retornam. Traduções obtidas de um CMS em tempo de execução precisam do CMS do Intlayer ou dos comandos intlayer pull / push.

    Manter as importações JSON no createI18n() mantém os 75 KB no bundle. Remova-as assim que o intlayer test passar com sucesso.

    7.9 KB contra 3.9 KB para o vue-intlayer. Assim que cada componente tiver migrado para o useIntlayer, remova-o.

    Quando usar qual?

    Sua aplicação depende dos blocos SFC <i18n>, de fluxos setLocaleMessage() em tempo de execução, ou 90 KB por página não é uma preocupação para o seu público.

    Você está no vue-i18n e deseja os 88 KB economizados, componentes 23 vezes menores, 0% de vazamento de página, chaves tipadas e verificações no CI sem editar um arquivo .vue. Este é o ponto de entrada para uma base de código vue-i18n existente.

    Para novos projetos, ou assim que o adaptador cumprir seu papel. Possui o runtime mais leve (3.9 KB) e o modelo .content.ts por componente que substitui blocos <i18n> por conteúdo tipado. Comece com Intlayer com Vue ou com Nuxt.

    Perguntas Frequentes

    Não. A compilação do benchmark modificou apenas o vite.config.ts, intlayer.config.ts e uma linha em src/i18n.ts, a importação de messages. Todas as chamadas para useI18n(), $t, v-t e Options API permaneceram inalteradas.

    Porque o useI18n() deixa de acessar a instância global. O createI18n({ messages }) contém todas as mensagens de todos os idiomas, então um componente compilado isoladamente arrasta 196 KB. Com o adaptador, ele acessa apenas seu próprio dicionário: 8.4 KB.

    Mantida. As configurações datetimeFormats e numberFormats passadas para createI18n() são respeitadas, suportadas pela API nativa Intl. Consulte formatação de data, hora e número.

    O @intlayer/vue-i18n é voltado para Vite + Vue. Para @nuxtjs/i18n, use o adaptador de compatibilidade Nuxt i18n e veja Intlayer com Nuxt para a configuração nativa.

    Sim. Qualquer componente pode mudar de useI18n() para useIntlayer("footer") com um arquivo de conteúdo co-localizado. Dicionários JSON e .content.ts coexistem e se mesclam.

    Comparações relacionadas

    Vue i18n library ecosystem

    Mesma série de adaptadores:

    Bibliotecas comparadas diretamente:

    Documentação de referência:

    Para entender de onde vêm essas bibliotecas, leia a história do i18n em JavaScript.

    Conclusão

    @intlayer/vue-i18n muda o que useI18n() está vinculado: de uma instância global contendo todas as mensagens de cada locale para um dicionário compilado para esse componente. No mesmo app Vite + Vue 3 que é 88 KB menor por página, um runtime 3x menor, componentes 23x menores e 0% vazamento de página, para um arquivo de config, uma linha de plugin e um import deletado. Blocos SFC <i18n> e carregamento de mensagens em tempo de execução são as duas coisas que não carrega, e o runtime vue-intlayer nativo permanece com metade do tamanho.

    Todos os dados brutos, os aplicativos de teste e os scripts estão no repositório Benchmark Bloom. Execute você mesmo.

    Consulte a documentação 'Por que Intlayer?' para mais detalhes.

    Comentários

    Ainda sem comentários. Seja o primeiro a compartilhar seus pensamentos.

    Artigos relacionados

    Últimos artigos