mapa-frontend-i18n
v2.0.0
Published
Biblioteca Angular de internacionalização (i18n) construída sobre [`@angular/localize`](https://angular.dev/guide/i18n). Fornece os catálogos de tradução do produto MAPA (pt-BR, en, es) e funções para inicializar o idioma da aplicação.
Readme
mapa-frontend-i18n
Biblioteca Angular de internacionalização (i18n) construída sobre
@angular/localize. Fornece os catálogos de
tradução do produto MAPA (pt-BR, en, es) e funções para inicializar o idioma da
aplicação.
A partir da 2.0, as traduções podem ser carregadas de arquivos JSON hospedados na AWS (S3/CloudFront), permitindo corrigir termos e acentuação sem republicar a lib nem fazer redeploy das aplicações. Os catálogos embutidos continuam no pacote como fallback.
Compatibilidade: a 2.0 é aditiva — a API síncrona da 1.x (
initializeAppLanguage,getMapaUiTexts, tipos e catálogos) continua igual. O carregamento remoto é opt-in viainitializeAppLanguageAsync. O major marca a mudança de arquitetura (traduções podem vir da rede), não uma quebra de API.
Sumário
- Instalação
- Como funciona
- Inicialização
- Opções de carregamento remoto
- Cache e revalidação
- Arquivos de tradução na AWS
- Gerar/atualizar os JSON
- Processo de hotfix de termo
- Requisitos de infraestrutura
- Outras APIs
- Idiomas suportados
Instalação
npm install mapa-frontend-i18npeerDependency: @angular/localize ~20.3.0 (já presente nas aplicações MAPA).
Como funciona
- As traduções são um mapa achatado
{ "chave.do.localize": "texto" }, injetado no@angular/localizevialoadTranslations(). - O
$localizeresolve cada mensagem no momento em que ela é avaliada, usando o mapa global. Por isso, as traduções precisam ser carregadas antes dobootstrapApplication()— caso contrário a tela aparece com o texto-fonte (pt-BR) ou com as chaves cruas. - O carregamento remoto faz o merge
{ ...embutido, ...remoto }: o remoto sobrescreve o embutido, e qualquer chave ausente no remoto ainda resolve pelo embutido. Ou seja, arquivos remotos parciais são seguros.
Inicialização
Modo 1 — Embutido (síncrono)
Usa apenas os catálogos embutidos no pacote. É o comportamento clássico, sem rede.
import { initializeAppLanguage } from "mapa-frontend-i18n";
initializeAppLanguage(); // lê o idioma do localStorage e carrega o embutidoModo 2 — Remoto com fallback (recomendado)
Carrega da AWS, com cache local e fallback automático para o embutido. Deve ser aguardado antes do bootstrap.
// main.ts
import { bootstrapApplication } from "@angular/platform-browser";
import { initializeAppLanguageAsync } from "mapa-frontend-i18n";
import { AppComponent } from "./app/app.component";
import { appConfig } from "./app/app.config";
import { environment } from "./environments/environment";
initializeAppLanguageAsync({ baseUrl: environment.i18nBaseUrl })
.catch(() => undefined) // nunca lança; em falha já usa o fallback embutido
.then(() => bootstrapApplication(AppComponent, appConfig));environment.i18nBaseUrl aponta para a pasta que contém os JSON por idioma, por
ambiente (dev/staging/prod). Ex.: https://cdn.mapa.com.br/i18n.
Modo 3 — Host distribui aos MFEs
Em micro-frontends, o host busca as traduções uma única vez e as repassa para
cada MFE, evitando um request por MFE. Use quando cada MFE roda em um contexto de
@angular/localize próprio.
// no host (antes do bootstrap):
import { getStoredAppLanguage, loadRemoteTranslations } from "mapa-frontend-i18n";
const language = getStoredAppLanguage();
const remote = await loadRemoteTranslations(language, {
baseUrl: environment.i18nBaseUrl,
});
// disponibilize `language` + `remote` para os MFEs (window, store compartilhado, etc.)// em cada MFE (antes do seu bootstrap):
import { initializeAppLanguageFromData } from "mapa-frontend-i18n";
initializeAppLanguageFromData(language, remote ?? {});Se os MFEs compartilham o mesmo
@angular/localize(singleton via module federation), basta o host chamarinitializeAppLanguageAsyncuma vez.
Opções de carregamento remoto
RemoteTranslationOptions (usado por initializeAppLanguageAsync e
loadRemoteTranslations):
| Opção | Tipo | Padrão | Descrição |
| ------------ | -------------------------- | -------------- | ------------------------------------------------------------------------- |
| baseUrl | string | — (obrigatório) | URL base dos JSON, sem barra final. Busca ${baseUrl}/${idioma}.json. |
| version | string | undefined | Tag de versão (?v=) para cache-busting manual (requer CloudFront com query string na chave de cache). |
| timeoutMs | number | 5000 | Aborta o request após esse tempo (o boot não trava se a AWS demorar). |
| fetchImpl | typeof fetch | globalThis.fetch | fetch customizado (testes/SSR). |
| onError | (error: unknown) => void | undefined | Notificação de erro de fetch/parse (não-fatal; o fallback é usado). |
Cache e revalidação
Estratégia stale-while-revalidate:
- Cache em
localStorage, chavei18n.cache.<idioma>(apenas o idioma ativo). - 1º acesso: aguarda o fetch; em falha, usa o embutido.
- Acessos seguintes: aplica o cache na hora (boot instantâneo) e revalida em segundo plano. Se o conteúdo mudou, o cache é atualizado e a nova versão entra no próximo reload.
O request é um GET simples (sem cabeçalhos customizados) com cache:
"no-cache": a revalidação contra o CDN é feita pelo próprio browser via
ETag/Last-Modified, de forma transparente. Isso evita preflight CORS — o
servidor só precisa do cabeçalho Access-Control-Allow-Origin.
Cadeia de fallback em qualquer falha: cache → catálogo embutido (nunca a
chave crua).
Arquivos de tradução na AWS
Um arquivo JSON por idioma, contendo o mapa achatado já mesclado de todos os módulos:
${baseUrl}/pt-BR.json
${baseUrl}/en.json
${baseUrl}/es.jsonFormato (mesmo shape consumido pelo loadTranslations()):
{
"common.cancel": "Cancelar",
"common.close": "Fechar",
"home.title": "Painel"
}Gerar/atualizar os JSON
O script de export gera os arquivos a partir dos catálogos embutidos. Use para semear o conteúdo inicial na AWS e para manter o fallback embutido sincronizado a cada release.
npm run export:i18n
# escreve dist/i18n-remote/{pt-BR,en,es}.jsonProcesso de hotfix de termo (sem republicar)
- Edite o termo/acentuação diretamente no JSON do idioma na AWS (ou ajuste só as chaves alteradas — arquivos parciais são suportados).
- Faça upload para o S3.
- Invalide o cache do CloudFront ou confie no
ETag+Cache-Controlcurto (a revalidação aplica a mudança no próximo reload dos usuários).
Nenhuma publicação de versão da lib nem redeploy das aplicações é necessária.
Os catálogos
*.tsembutidos seguem como fallback (último-bom-conhecido). Para mantê-los alinhados com a AWS, rodenpm run export:i18nperiodicamente e publique uma release quando quiser atualizar o fallback.
Requisitos de infraestrutura (AWS)
- S3 (com ou sem CloudFront) servindo
/i18n/{idioma}.jsonpor ambiente. - CORS (obrigatório): basta
Access-Control-Allow-Originliberando as origens das aplicações no GET (não há preflight, pois o request é simples). Bucket público não dispensa CORS — sem ele o browser bloqueia a leitura viafetch. Ver config de exemplo emexamples/aws/s3-cors.json. ETag(o S3 já envia) para a revalidação condicional do browser funcionar.- Propagação de hotfix:
- S3 direto (sem CDN): a alteração é imediata no próximo reload.
- Via CloudFront: use
Cache-Controlcurto (ex.:max-age=300) ou invalide o caminho a cada hotfix — senão o edge serve a versão antiga até a TTL padrão (24h).
Outras APIs
import {
// Idioma
getStoredAppLanguage,
persistAppLanguage,
applyDocumentLanguage,
getAngularLocale,
getIntlLocale,
resolveAppLanguage,
APP_LANGUAGE_OPTIONS,
DEFAULT_APP_LANGUAGE,
// Textos de UI (validações, paginador, etc.)
getMapaUiTexts,
applyPaginatorIntl,
} from "mapa-frontend-i18n";getMapaUiTexts(language) retorna textos de UI reutilizáveis (filtros, tabela,
validações). Alguns valores são funções (paginador e validações com
interpolação) e permanecem no código da lib — não são externalizados para JSON.
Idiomas suportados
| Código | Idioma | Padrão |
| ------- | ---------- | ------ |
| pt-BR | Português | ✅ (fonte/fallback) |
| es | Español | |
| en | English | |
O idioma escolhido é persistido em localStorage na chave menu.language.
