Autor:
    Criação:2025-03-13Última atualização:2026-06-21

    Sync JSON (pontes i18n) - Sync JSON com suporte ICU / i18next

    www.youtube.com

    Use o Intlayer como um complemento para sua pilha i18n existente. Este plugin mantém suas mensagens JSON sincronizadas com os dicionários Intlayer para que você possa:

    • Manter i18next, next-intl, react-intl, vue-i18n, next-translate, nuxt-i18n, Solid-i18next, svelte-i18n, etc.
    • Gerenciar e traduzir suas mensagens com o Intlayer (CLI, CI, provedores, CMS), sem precisar refatorar seu aplicativo.
    • Publicar tutoriais e conteúdo SEO direcionado a cada ecossistema, enquanto sugere o Intlayer como a camada de gerenciamento JSON.

    Notas e escopo atual:

    • A externalização para o CMS funciona para traduções e texto clássico.
    • Ainda não há suporte para inserções, plurais/ICU ou recursos avançados de tempo de execução de outras bibliotecas.
    • O editor visual ainda não é suportado para saídas i18n de terceiros.

    Quando usar este plugin

    • Você já usa uma biblioteca i18n e armazena mensagens em arquivos JSON.
    • Você deseja preenchimento assistido por IA, testes em CI e operações de conteúdo sem alterar seu tempo de execução de renderização.

    Instalação

    bash
    pnpm add -D @intlayer/sync-json-plugin# ounpm i -D @intlayer/sync-json-plugin

    Plugins

    Este pacote fornece dois plugins:

    • loadJSON: Carrega arquivos JSON em dicionários Intlayer.

      • Este plugin é usado para carregar arquivos JSON de uma fonte e será carregado em dicionários Intlayer. Ele pode escanear todo o codebase e procurar por arquivos JSON específicos. Este plugin pode ser usado
        • se você usa uma biblioteca i18n que impõe um local específico para seus JSONs serem carregados (ex: next-intl, i18next, react-intl, vue-i18n, etc.), mas você quer colocar sua declaração de conteúdo onde quiser em seu codebase.
        • Também pode ser usado se você quiser buscar suas mensagens de uma fonte remota (ex: um CMS, uma API, etc.) e armazenar suas mensagens em arquivos JSON.

      Por baixo dos panos, este plugin escaneará todo o codebase e procurará por arquivos JSON específicos e os carregará em dicionários Intlayer. Note que este plugin não escreverá a saída e as traduções de volta para os arquivos JSON.

    • syncJSON: Sincroniza arquivos JSON com dicionários Intlayer.

      • Este plugin é usado para sincronizar arquivos JSON com dicionários Intlayer. Ele pode escanear o local fornecido e carregar o JSON que corresponde ao padrão para arquivos JSON específicos. Este plugin é útil se você quiser obter os benefícios do Intlayer enquanto usa outra biblioteca i18n.

    Usando ambos os plugins

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";import { loadJSON, syncJSON } from "@intlayer/sync-json-plugin";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],    defaultLocale: Locales.ENGLISH,  },  // Mantenha seus arquivos JSON atuais sincronizados com os dicionários Intlayer  plugins: [    /**     * Irá carregar todos os arquivos JSON na src que correspondem ao padrão {key}.i18n json     */    loadJSON({      source: ({ key }) => `./src/**/${key}.i18n.json`,      locale: Locales.ENGLISH,      priority: 1, // Garante que esses arquivos JSON tenham precedência sobre os arquivos em `./locales/en/${key}.json`      format: "intlayer", // Formato do conteúdo JSON    }),    /**     * Irá carregar e escrever a saída e as traduções de volta para os arquivos JSON no diretório de localidades     */    syncJSON({      source: ({ key, locale }) => `./locales/${locale}/${key}.json`,      priority: 0,      format: "i18next",    }),  ],};export default config;

    syncJSON plugin

    Início rápido

    Adicione o plugin ao seu intlayer.config.ts e aponte para sua estrutura JSON existente.

    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,  },  // Mantenha seus arquivos JSON atuais sincronizados com os dicionários Intlayer  plugins: [    syncJSON({      // Layout por localidade, por namespace (por exemplo, next-intl, i18next com namespaces)      source: ({ key, locale }) => `./locales/${locale}/${key}.json`,      format: "icu",    }),  ],};export default config;

    Alternativa: arquivo único por localidade (comum em configurações i18next/react-intl):

    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],    defaultLocale: Locales.ENGLISH,  },  plugins: [    syncJSON({      format: "i18next",      source: ({ locale }) => `./locales/${locale}.json`,      format: "i18next",    }),  ],};export default config;

    Como funciona

    • Leitura: o plugin descobre arquivos JSON a partir do seu construtor source e os carrega como dicionários Intlayer.
    • Escrita: após builds e preenchimentos, ele grava o JSON localizado de volta nos mesmos caminhos (com uma nova linha final para evitar problemas de formatação).
    • Auto-preenchimento: o plugin declara um caminho autoFill para cada dicionário. Executar intlayer fill atualiza apenas as traduções ausentes nos seus arquivos JSON por padrão.

    API:

    ts
    syncJSON({  source: ({ key, locale }) => string, // obrigatório  location?: string, // rótulo opcional, padrão: "plugin"  priority?: number, // prioridade opcional para resolução de conflitos, padrão: 0  format?: 'intlayer' | 'icu' | 'i18next', // formatador opcional, usado para compatibilidade com o runtime Intlayer  splitKeys?: boolean, // opcional, divide um único arquivo em um dicionário por chave de nível superior (detecção automática)});

    format ('intlayer' | 'icu' | 'i18next')

    Especifica o formatador a ser usado para o conteúdo do dicionário ao sincronizar arquivos JSON. Isso permite usar diferentes sintaxes de formatação de mensagens compatíveis com o runtime Intlayer.

    • undefined: Nenhum formatador será usado, o conteúdo JSON será usado como está.
    • 'intlayer': O formatador Intlayer padrão (padrão).
    • 'icu': Usa formatação de mensagens ICU (compatível com bibliotecas como react-intl, vue-i18n).
    • 'i18next': Usa formatação de mensagens i18next (compatível com i18next, next-i18next, Solid-i18next).

    Note que usar um formatador transformará seu conteúdo JSON na entrada e saída. Para regras JSON complexas como plurais ICU, o parsing pode não garantir um mapeamento 1 para 1 entre entrada e saída. Se você não usar o runtime Intlayer, pode preferir não definir um formatador.

    Exemplo:

    ts
    syncJSON({  source: ({ key, locale }) => `./locales/${locale}/${key}.json`,  format: "i18next", // Usar formatação i18next para compatibilidade}),

    splitKeys (boolean)

    Controla se um único arquivo JSON cujas chaves de primeiro nível são namespaces deve se tornar um dicionário por chave de nível superior, em vez de um único dicionário contendo o arquivo inteiro.

    Isso corresponde ao modelo de namespace de bibliotecas como next-intl e react-intl, onde um arquivo messages/{locale}.json agrupa vários namespaces por suas chaves de primeiro nível, cada um endereçado independentemente (por exemplo, useTranslations('Hero') resolve para o dicionário Hero).

    • undefined (padrão): detecção automática — o arquivo é dividido quando o padrão source não possui um segmento {key} (um arquivo contém todos os namespaces), e mantido como um único dicionário caso contrário (um arquivo por chave).
    • true: sempre divide cada chave de nível superior em seu próprio dicionário.
    • false: nunca divide; o arquivo inteiro se torna um único dicionário.

    Dado um único arquivo messages/{locale}.json:

    messages/en.json
    {  "Hero": { "title": "Full-stack developer" },  "Nav": { "work": "Work", "about": "About" },  "About": { "lead": "I build apps end to end." }}
    intlayer.config.ts
    syncJSON({  format: "icu",  source: ({ locale }) => `./messages/${locale}.json`,  // splitKeys: true, // implícito porque o padrão não possui um segmento `{key}`}),

    Isso produz três dicionários — Hero, Nav e About — de modo que useTranslations('Hero') (next-intl) resolve corretamente. Na gravação de volta, todos os namespaces são remontados no mesmo arquivo por localidade.

    Quando você mantém o segmento {key} explícito em sua source (por exemplo, ./locales/${locale}/${key}.json), cada arquivo já é um namespace, então a divisão é desativada por padrão.

    Múltiplas fontes JSON e prioridade

    Você pode adicionar múltiplos plugins syncJSON para sincronizar diferentes fontes JSON. Isso é útil quando você tem múltiplas bibliotecas i18n ou diferentes estruturas JSON no seu projeto.

    Sistema de prioridade

    Quando múltiplos plugins têm como alvo a mesma chave de dicionário, o parâmetro priority determina qual plugin tem precedência:

    • Números de prioridade mais altos ganham sobre os mais baixos
    • Prioridade padrão dos arquivos .content é 0
    • Prioridade padrão dos plugins é 0
    • Plugins com a mesma prioridade são processados na ordem em que aparecem na configuraçã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],    defaultLocale: Locales.ENGLISH,  },  plugins: [    // Fonte JSON principal (maior prioridade)    syncJSON({      format: "i18next",      source: ({ key, locale }) => `./locales/${locale}/${key}.json`,      location: "main-translations",      priority: 10,    }),    // Fonte JSON de fallback (prioridade menor)    syncJSON({      format: "i18next",      source: ({ locale }) => `./fallback-locales/${locale}.json`,      location: "fallback-translations",      priority: 5,    }),    // Fonte JSON legado (prioridade mais baixa)    syncJSON({      format: "i18next",      source: ({ locale }) => `/my/other/app/legacy/${locale}/messages.json`,      location: "legacy-translations",      priority: 1,    }),  ],};export default config;

    Load JSON plugin

    Início rápido

    Adicione o plugin ao seu intlayer.config.ts para ingerir arquivos JSON existentes como dicionários Intlayer. Este plugin é somente leitura (não grava em disco):

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";import { loadJSON } from "@intlayer/sync-json-plugin";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.FRENCH, Locales.SPANISH],    defaultLocale: Locales.ENGLISH,  },  plugins: [    // Ingerir mensagens JSON localizadas em qualquer lugar da sua árvore de origem    loadJSON({      source: ({ key }) => `./src/**/${key}.i18n.json`,      // Carregar uma única localidade por instância de plugin (padrão para a defaultLocale da configuração)      locale: Locales.ENGLISH,      priority: 0,    }),  ],};export default config;

    Alternativa: layout por localidade, ainda somente leitura (apenas a localidade selecionada é carregada):

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";import { loadJSON } from "@intlayer/sync-json-plugin";const config: IntlayerConfig = {  internationalization: {    locales: [Locales.ENGLISH, Locales.FRENCH],    defaultLocale: Locales.ENGLISH,  },  plugins: [    loadJSON({      // Apenas arquivos para Locales.FRENCH serão carregados a partir deste padrão      source: ({ key, locale }) => `./locales/${locale}/${key}.json`,      locale: Locales.FRENCH,    }),  ],};export default config;

    Como funciona

    • Descobrir: constrói um glob a partir do seu construtor source e coleta arquivos JSON correspondentes.
    • Ingerir: carrega cada arquivo JSON como um dicionário Intlayer com a locale fornecida.
    • Somente leitura: não escreve ou formata arquivos de saída; use syncJSON se precisar de sincronização de ida e volta.
    • Pronto para auto-preenchimento: define um padrão fill para que intlayer content fill possa preencher chaves ausentes.

    API

    ts
    loadJSON({  // Constrói caminhos para o seu JSON. `locale` é opcional se sua estrutura não tiver um segmento de localidade  source: ({ key, locale }) => string,  // Localidade de destino para os dicionários carregados por esta instância de plugin  // Padrão para configuration.internationalization.defaultLocale  locale?: Locale,  // Rótulo opcional para identificar a fonte  location?: string, // padrão: "plugin"  // Prioridade usada para resolução de conflitos contra outras fontes  priority?: number, // padrão: 0  // Formatador opcional para o conteúdo JSON  format?: 'intlayer' | 'icu' | 'i18next', // padrão: 'intlayer'  // Divide um único arquivo em um dicionário por chave de nível superior (detecção automática)  splitKeys?: boolean,});

    format ('intlayer' | 'icu' | 'i18next')

    Especifica o formatador a ser usado para o conteúdo do dicionário ao carregar arquivos JSON. Isso permite usar diferentes sintaxes de formatação de mensagens compatíveis com várias bibliotecas i18n.

    • 'intlayer': O formatador Intlayer padrão (padrão).
    • 'icu': Usa formatação de mensagens ICU (compatível com bibliotecas como react-intl, vue-i18n).
    • 'i18next': Usa formatação de mensagens i18next (compatível com i18next, next-i18next, Solid-i18next).

    Exemplo:

    ts
    loadJSON({  source: ({ key }) => `./src/**/${key}.i18n.json`,  locale: Locales.ENGLISH,  format: "icu", // Usar formatação ICU para compatibilidade}),

    splitKeys (boolean)

    Mesmo comportamento que em syncJSON: quando um único arquivo JSON agrupa vários namespaces por suas chaves de primeiro nível, cada chave de nível superior se torna seu próprio dicionário.

    • undefined (padrão): detecção automática — divide quando o padrão source não possui um segmento {key}, dicionário único caso contrário.
    • true / false: força ou desabilita a divisão.
    ts
    loadJSON({  source: ({ locale }) => `./messages/${locale}.json`,  format: "icu",  // splitKeys auto-enabled: `Hero`, `Nav`, `About`, … cada um se torna um dicionário}),

    Comportamento e convenções

    • Se sua máscara source incluir um placeholder de localidade, apenas os arquivos para a locale selecionada serão ingeridos.
    • Se não houver um segmento {key} em sua máscara, cada chave de nível superior do arquivo se torna seu próprio dicionário por padrão (veja splitKeys). Defina splitKeys: false para carregar o arquivo inteiro como um único dicionário index.
    • As chaves são derivadas dos caminhos dos arquivos substituindo o placeholder {key} em seu construtor source.
    • O plugin usa apenas arquivos descobertos e não fabrica localidades ou chaves ausentes.
    • O caminho fill é inferido de sua source e usado para atualizar valores ausentes via CLI quando você opta por isso.

    Resolução de conflitos

    Quando a mesma chave de tradução existe em múltiplas fontes JSON:

    1. O plugin com a maior prioridade determina o valor final
    2. Fontes com prioridade menor são usadas como fallbacks para chaves ausentes
    3. Isso permite manter traduções legadas enquanto migra gradualmente para novas estruturas

    CLI

    Os arquivos JSON sincronizados serão considerados como outros arquivos .content. Isso significa que todos os comandos do intlayer estarão disponíveis para os arquivos JSON sincronizados. Incluindo:

    • intlayer content test para testar se há traduções faltando
    • intlayer content list para listar os arquivos JSON sincronizados
    • intlayer content fill para preencher as traduções faltantes
    • intlayer content push para enviar os arquivos JSON sincronizados
    • intlayer content pull para puxar os arquivos JSON sincronizados

    Veja Intlayer CLI para mais detalhes.

    Limitações (atuais)

    • Sem suporte para inserções ou plurais/ICU ao direcionar bibliotecas de terceiros.
    • Editor visual ainda não disponível para runtimes que não sejam Intlayer.
    • Sincronização apenas de JSON; formatos de catálogo não JSON não são suportados.

    Por que isso importa

    • Podemos recomendar soluções i18n consolidadas e posicionar o Intlayer como um complemento.
    • Aproveitamos o SEO/palavras-chave delas com tutoriais que terminam sugerindo o Intlayer para gerenciar JSON.
    • Expande o público-alvo de “novos projetos” para “qualquer equipe que já usa i18n”.