mapa-frontend-i18n 1.3.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +264 -0
- package/fesm2022/mapa-frontend-i18n.mjs +247 -5
- package/fesm2022/mapa-frontend-i18n.mjs.map +1 -1
- package/index.d.ts +78 -2
- package/mapa-frontend-i18n-2.0.0.tgz +0 -0
- package/package.json +1 -1
- package/mapa-frontend-i18n-1.3.4.tgz +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# mapa-frontend-i18n
|
|
2
|
+
|
|
3
|
+
Biblioteca Angular de internacionalização (i18n) construída sobre
|
|
4
|
+
[`@angular/localize`](https://angular.dev/guide/i18n). Fornece os catálogos de
|
|
5
|
+
tradução do produto MAPA (pt-BR, en, es) e funções para inicializar o idioma da
|
|
6
|
+
aplicação.
|
|
7
|
+
|
|
8
|
+
A partir da **2.0**, as traduções podem ser carregadas de **arquivos JSON
|
|
9
|
+
hospedados na AWS (S3/CloudFront)**, permitindo corrigir termos e acentuação
|
|
10
|
+
**sem republicar a lib nem fazer redeploy das aplicações**. Os catálogos
|
|
11
|
+
embutidos continuam no pacote como _fallback_.
|
|
12
|
+
|
|
13
|
+
> **Compatibilidade:** a 2.0 é aditiva — a API síncrona da 1.x
|
|
14
|
+
> (`initializeAppLanguage`, `getMapaUiTexts`, tipos e catálogos) continua igual.
|
|
15
|
+
> O carregamento remoto é **opt-in** via `initializeAppLanguageAsync`. O major
|
|
16
|
+
> marca a mudança de arquitetura (traduções podem vir da rede), não uma quebra
|
|
17
|
+
> de API.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Sumário
|
|
22
|
+
|
|
23
|
+
- [Instalação](#instalação)
|
|
24
|
+
- [Como funciona](#como-funciona)
|
|
25
|
+
- [Inicialização](#inicialização)
|
|
26
|
+
- [Modo 1 — Embutido (síncrono)](#modo-1--embutido-síncrono)
|
|
27
|
+
- [Modo 2 — Remoto com fallback (recomendado)](#modo-2--remoto-com-fallback-recomendado)
|
|
28
|
+
- [Modo 3 — Host distribui aos MFEs](#modo-3--host-distribui-aos-mfes)
|
|
29
|
+
- [Opções de carregamento remoto](#opções-de-carregamento-remoto)
|
|
30
|
+
- [Cache e revalidação](#cache-e-revalidação)
|
|
31
|
+
- [Arquivos de tradução na AWS](#arquivos-de-tradução-na-aws)
|
|
32
|
+
- [Gerar/atualizar os JSON](#geraratualizar-os-json)
|
|
33
|
+
- [Processo de hotfix de termo](#processo-de-hotfix-de-termo-sem-republicar)
|
|
34
|
+
- [Requisitos de infraestrutura](#requisitos-de-infraestrutura-aws)
|
|
35
|
+
- [Outras APIs](#outras-apis)
|
|
36
|
+
- [Idiomas suportados](#idiomas-suportados)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Instalação
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install mapa-frontend-i18n
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`peerDependency`: `@angular/localize ~20.3.0` (já presente nas aplicações MAPA).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Como funciona
|
|
51
|
+
|
|
52
|
+
- As traduções são um **mapa achatado** `{ "chave.do.localize": "texto" }`,
|
|
53
|
+
injetado no `@angular/localize` via `loadTranslations()`.
|
|
54
|
+
- O `$localize` resolve cada mensagem **no momento em que ela é avaliada**, usando
|
|
55
|
+
o mapa global. Por isso, **as traduções precisam ser carregadas antes do
|
|
56
|
+
`bootstrapApplication()`** — caso contrário a tela aparece com o texto-fonte
|
|
57
|
+
(pt-BR) ou com as chaves cruas.
|
|
58
|
+
- O carregamento remoto faz o merge `{ ...embutido, ...remoto }`: o remoto
|
|
59
|
+
**sobrescreve** o embutido, e qualquer chave ausente no remoto ainda resolve
|
|
60
|
+
pelo embutido. Ou seja, **arquivos remotos parciais são seguros**.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Inicialização
|
|
65
|
+
|
|
66
|
+
### Modo 1 — Embutido (síncrono)
|
|
67
|
+
|
|
68
|
+
Usa apenas os catálogos embutidos no pacote. É o comportamento clássico, sem
|
|
69
|
+
rede.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { initializeAppLanguage } from "mapa-frontend-i18n";
|
|
73
|
+
|
|
74
|
+
initializeAppLanguage(); // lê o idioma do localStorage e carrega o embutido
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Modo 2 — Remoto com fallback (recomendado)
|
|
78
|
+
|
|
79
|
+
Carrega da AWS, com cache local e _fallback_ automático para o embutido. **Deve
|
|
80
|
+
ser aguardado antes do bootstrap.**
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// main.ts
|
|
84
|
+
import { bootstrapApplication } from "@angular/platform-browser";
|
|
85
|
+
import { initializeAppLanguageAsync } from "mapa-frontend-i18n";
|
|
86
|
+
import { AppComponent } from "./app/app.component";
|
|
87
|
+
import { appConfig } from "./app/app.config";
|
|
88
|
+
import { environment } from "./environments/environment";
|
|
89
|
+
|
|
90
|
+
initializeAppLanguageAsync({ baseUrl: environment.i18nBaseUrl })
|
|
91
|
+
.catch(() => undefined) // nunca lança; em falha já usa o fallback embutido
|
|
92
|
+
.then(() => bootstrapApplication(AppComponent, appConfig));
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`environment.i18nBaseUrl` aponta para a pasta que contém os JSON por idioma, por
|
|
96
|
+
ambiente (dev/staging/prod). Ex.: `https://cdn.mapa.com.br/i18n`.
|
|
97
|
+
|
|
98
|
+
### Modo 3 — Host distribui aos MFEs
|
|
99
|
+
|
|
100
|
+
Em micro-frontends, o **host** busca as traduções uma única vez e as repassa para
|
|
101
|
+
cada MFE, evitando um request por MFE. Use quando cada MFE roda em um contexto de
|
|
102
|
+
`@angular/localize` próprio.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// no host (antes do bootstrap):
|
|
106
|
+
import { getStoredAppLanguage, loadRemoteTranslations } from "mapa-frontend-i18n";
|
|
107
|
+
|
|
108
|
+
const language = getStoredAppLanguage();
|
|
109
|
+
const remote = await loadRemoteTranslations(language, {
|
|
110
|
+
baseUrl: environment.i18nBaseUrl,
|
|
111
|
+
});
|
|
112
|
+
// disponibilize `language` + `remote` para os MFEs (window, store compartilhado, etc.)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// em cada MFE (antes do seu bootstrap):
|
|
117
|
+
import { initializeAppLanguageFromData } from "mapa-frontend-i18n";
|
|
118
|
+
|
|
119
|
+
initializeAppLanguageFromData(language, remote ?? {});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
> Se os MFEs compartilham o mesmo `@angular/localize` (singleton via module
|
|
123
|
+
> federation), basta o host chamar `initializeAppLanguageAsync` uma vez.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Opções de carregamento remoto
|
|
128
|
+
|
|
129
|
+
`RemoteTranslationOptions` (usado por `initializeAppLanguageAsync` e
|
|
130
|
+
`loadRemoteTranslations`):
|
|
131
|
+
|
|
132
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
133
|
+
| ------------ | -------------------------- | -------------- | ------------------------------------------------------------------------- |
|
|
134
|
+
| `baseUrl` | `string` | — (obrigatório) | URL base dos JSON, sem barra final. Busca `${baseUrl}/${idioma}.json`. |
|
|
135
|
+
| `version` | `string` | `undefined` | Tag de versão (`?v=`) para _cache-busting_ manual (requer CloudFront com query string na chave de cache). |
|
|
136
|
+
| `timeoutMs` | `number` | `5000` | Aborta o request após esse tempo (o boot não trava se a AWS demorar). |
|
|
137
|
+
| `fetchImpl` | `typeof fetch` | `globalThis.fetch` | `fetch` customizado (testes/SSR). |
|
|
138
|
+
| `onError` | `(error: unknown) => void` | `undefined` | Notificação de erro de fetch/parse (não-fatal; o _fallback_ é usado). |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Cache e revalidação
|
|
143
|
+
|
|
144
|
+
Estratégia _stale-while-revalidate_:
|
|
145
|
+
|
|
146
|
+
- Cache em `localStorage`, chave `i18n.cache.<idioma>` (apenas o idioma ativo).
|
|
147
|
+
- **1º acesso:** aguarda o fetch; em falha, usa o embutido.
|
|
148
|
+
- **Acessos seguintes:** aplica o cache **na hora** (boot instantâneo) e revalida
|
|
149
|
+
em segundo plano. Se o conteúdo mudou, o cache é atualizado e **a nova versão
|
|
150
|
+
entra no próximo reload**.
|
|
151
|
+
|
|
152
|
+
O request é um **GET simples** (sem cabeçalhos customizados) com `cache:
|
|
153
|
+
"no-cache"`: a revalidação contra o CDN é feita pelo próprio browser via
|
|
154
|
+
`ETag`/`Last-Modified`, de forma transparente. Isso evita _preflight_ CORS — o
|
|
155
|
+
servidor só precisa do cabeçalho `Access-Control-Allow-Origin`.
|
|
156
|
+
|
|
157
|
+
Cadeia de _fallback_ em qualquer falha: `cache → catálogo embutido` (nunca a
|
|
158
|
+
chave crua).
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Arquivos de tradução na AWS
|
|
163
|
+
|
|
164
|
+
Um arquivo JSON **por idioma**, contendo o mapa achatado já mesclado de todos os
|
|
165
|
+
módulos:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
${baseUrl}/pt-BR.json
|
|
169
|
+
${baseUrl}/en.json
|
|
170
|
+
${baseUrl}/es.json
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Formato (mesmo shape consumido pelo `loadTranslations()`):
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"common.cancel": "Cancelar",
|
|
178
|
+
"common.close": "Fechar",
|
|
179
|
+
"home.title": "Painel"
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Gerar/atualizar os JSON
|
|
186
|
+
|
|
187
|
+
O script de export gera os arquivos a partir dos catálogos embutidos. Use para
|
|
188
|
+
**semear** o conteúdo inicial na AWS e para manter o _fallback_ embutido
|
|
189
|
+
sincronizado a cada release.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npm run export:i18n
|
|
193
|
+
# escreve dist/i18n-remote/{pt-BR,en,es}.json
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Processo de hotfix de termo (sem republicar)
|
|
199
|
+
|
|
200
|
+
1. Edite o termo/acentuação diretamente no JSON do idioma na AWS (ou ajuste só as
|
|
201
|
+
chaves alteradas — arquivos parciais são suportados).
|
|
202
|
+
2. Faça upload para o S3.
|
|
203
|
+
3. Invalide o cache do CloudFront **ou** confie no `ETag` + `Cache-Control` curto
|
|
204
|
+
(a revalidação aplica a mudança no próximo reload dos usuários).
|
|
205
|
+
|
|
206
|
+
Nenhuma publicação de versão da lib nem redeploy das aplicações é necessária.
|
|
207
|
+
|
|
208
|
+
> Os catálogos `*.ts` embutidos seguem como _fallback_ (último-bom-conhecido).
|
|
209
|
+
> Para mantê-los alinhados com a AWS, rode `npm run export:i18n` periodicamente e
|
|
210
|
+
> publique uma release quando quiser atualizar o _fallback_.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Requisitos de infraestrutura (AWS)
|
|
215
|
+
|
|
216
|
+
- **S3** (com ou sem CloudFront) servindo `/i18n/{idioma}.json` por ambiente.
|
|
217
|
+
- **CORS (obrigatório):** basta `Access-Control-Allow-Origin` liberando as origens
|
|
218
|
+
das aplicações no GET (não há _preflight_, pois o request é simples). Bucket
|
|
219
|
+
público **não** dispensa CORS — sem ele o browser bloqueia a leitura via `fetch`.
|
|
220
|
+
Ver config de exemplo em [`examples/aws/s3-cors.json`](../../examples/aws/s3-cors.json).
|
|
221
|
+
- **`ETag`** (o S3 já envia) para a revalidação condicional do browser funcionar.
|
|
222
|
+
- **Propagação de hotfix:**
|
|
223
|
+
- **S3 direto (sem CDN):** a alteração é **imediata** no próximo reload.
|
|
224
|
+
- **Via CloudFront:** use `Cache-Control` curto (ex.: `max-age=300`) **ou
|
|
225
|
+
invalide** o caminho a cada hotfix — senão o edge serve a versão antiga até a
|
|
226
|
+
TTL padrão (24h).
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Outras APIs
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import {
|
|
234
|
+
// Idioma
|
|
235
|
+
getStoredAppLanguage,
|
|
236
|
+
persistAppLanguage,
|
|
237
|
+
applyDocumentLanguage,
|
|
238
|
+
getAngularLocale,
|
|
239
|
+
getIntlLocale,
|
|
240
|
+
resolveAppLanguage,
|
|
241
|
+
APP_LANGUAGE_OPTIONS,
|
|
242
|
+
DEFAULT_APP_LANGUAGE,
|
|
243
|
+
|
|
244
|
+
// Textos de UI (validações, paginador, etc.)
|
|
245
|
+
getMapaUiTexts,
|
|
246
|
+
applyPaginatorIntl,
|
|
247
|
+
} from "mapa-frontend-i18n";
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`getMapaUiTexts(language)` retorna textos de UI reutilizáveis (filtros, tabela,
|
|
251
|
+
validações). Alguns valores são **funções** (paginador e validações com
|
|
252
|
+
interpolação) e permanecem no código da lib — não são externalizados para JSON.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Idiomas suportados
|
|
257
|
+
|
|
258
|
+
| Código | Idioma | Padrão |
|
|
259
|
+
| ------- | ---------- | ------ |
|
|
260
|
+
| `pt-BR` | Português | ✅ (fonte/fallback) |
|
|
261
|
+
| `es` | Español | |
|
|
262
|
+
| `en` | English | |
|
|
263
|
+
|
|
264
|
+
O idioma escolhido é persistido em `localStorage` na chave `menu.language`.
|
|
@@ -4119,6 +4119,18 @@ const PT_RUNTIME_TRANSLATIONS$5 = {
|
|
|
4119
4119
|
"common.failure": "Falha!",
|
|
4120
4120
|
"common.filters": "Filtros",
|
|
4121
4121
|
"common.moneyPlaceholder": "0,00",
|
|
4122
|
+
"common.month.april": "abril",
|
|
4123
|
+
"common.month.august": "agosto",
|
|
4124
|
+
"common.month.december": "dezembro",
|
|
4125
|
+
"common.month.february": "fevereiro",
|
|
4126
|
+
"common.month.january": "janeiro",
|
|
4127
|
+
"common.month.july": "julho",
|
|
4128
|
+
"common.month.june": "junho",
|
|
4129
|
+
"common.month.march": "março",
|
|
4130
|
+
"common.month.may": "maio",
|
|
4131
|
+
"common.month.november": "novembro",
|
|
4132
|
+
"common.month.october": "outubro",
|
|
4133
|
+
"common.month.september": "setembro",
|
|
4122
4134
|
"common.no": "Não",
|
|
4123
4135
|
"common.observation": "Observações",
|
|
4124
4136
|
"common.search.company": "Pesquisar empresa",
|
|
@@ -4441,7 +4453,8 @@ const PT_RUNTIME_TRANSLATIONS$5 = {
|
|
|
4441
4453
|
"statement.list.section.title": "Movimentações",
|
|
4442
4454
|
"statement.page.description": "Consulte seu histórico de uso na plataforma.",
|
|
4443
4455
|
"statement.page.title": "Extrato de uso",
|
|
4444
|
-
"statement.transaction.
|
|
4456
|
+
"statement.transaction.usedEvaluationSingular": "1 avaliação utilizada",
|
|
4457
|
+
"statement.transaction.usedEvaluationsPlural": "{$INTERPOLATION} avaliações utilizadas",
|
|
4445
4458
|
"statement.summary.availableCredits": "Créditos disponíveis",
|
|
4446
4459
|
"statement.summary.contractInfo": "Informações Contratuais",
|
|
4447
4460
|
"statement.summary.lastPurchase": "Última compra",
|
|
@@ -5305,6 +5318,54 @@ const MISSING_RUNTIME_TRANSLATIONS$3 = {
|
|
|
5305
5318
|
en: "0.00",
|
|
5306
5319
|
es: "0,00",
|
|
5307
5320
|
},
|
|
5321
|
+
"common.month.april": {
|
|
5322
|
+
en: "april",
|
|
5323
|
+
es: "abril",
|
|
5324
|
+
},
|
|
5325
|
+
"common.month.august": {
|
|
5326
|
+
en: "august",
|
|
5327
|
+
es: "agosto",
|
|
5328
|
+
},
|
|
5329
|
+
"common.month.december": {
|
|
5330
|
+
en: "december",
|
|
5331
|
+
es: "diciembre",
|
|
5332
|
+
},
|
|
5333
|
+
"common.month.february": {
|
|
5334
|
+
en: "february",
|
|
5335
|
+
es: "febrero",
|
|
5336
|
+
},
|
|
5337
|
+
"common.month.january": {
|
|
5338
|
+
en: "january",
|
|
5339
|
+
es: "enero",
|
|
5340
|
+
},
|
|
5341
|
+
"common.month.july": {
|
|
5342
|
+
en: "july",
|
|
5343
|
+
es: "julio",
|
|
5344
|
+
},
|
|
5345
|
+
"common.month.june": {
|
|
5346
|
+
en: "june",
|
|
5347
|
+
es: "junio",
|
|
5348
|
+
},
|
|
5349
|
+
"common.month.march": {
|
|
5350
|
+
en: "march",
|
|
5351
|
+
es: "marzo",
|
|
5352
|
+
},
|
|
5353
|
+
"common.month.may": {
|
|
5354
|
+
en: "may",
|
|
5355
|
+
es: "mayo",
|
|
5356
|
+
},
|
|
5357
|
+
"common.month.november": {
|
|
5358
|
+
en: "november",
|
|
5359
|
+
es: "noviembre",
|
|
5360
|
+
},
|
|
5361
|
+
"common.month.october": {
|
|
5362
|
+
en: "october",
|
|
5363
|
+
es: "octubre",
|
|
5364
|
+
},
|
|
5365
|
+
"common.month.september": {
|
|
5366
|
+
en: "september",
|
|
5367
|
+
es: "septiembre",
|
|
5368
|
+
},
|
|
5308
5369
|
"common.no": {
|
|
5309
5370
|
en: "No",
|
|
5310
5371
|
es: "No",
|
|
@@ -6593,9 +6654,13 @@ const MISSING_RUNTIME_TRANSLATIONS$3 = {
|
|
|
6593
6654
|
en: "Usage statement",
|
|
6594
6655
|
es: "Extracto de uso",
|
|
6595
6656
|
},
|
|
6596
|
-
"statement.transaction.
|
|
6597
|
-
en: "
|
|
6598
|
-
es: "
|
|
6657
|
+
"statement.transaction.usedEvaluationSingular": {
|
|
6658
|
+
en: "1 used evaluation",
|
|
6659
|
+
es: "1 evaluación utilizada",
|
|
6660
|
+
},
|
|
6661
|
+
"statement.transaction.usedEvaluationsPlural": {
|
|
6662
|
+
en: "{$INTERPOLATION} used evaluations",
|
|
6663
|
+
es: "{$INTERPOLATION} evaluaciones utilizadas",
|
|
6599
6664
|
},
|
|
6600
6665
|
"statement.summary.availableCredits": {
|
|
6601
6666
|
en: "Available credits",
|
|
@@ -15264,6 +15329,125 @@ function getAppLanguageOption(language) {
|
|
|
15264
15329
|
?? APP_LANGUAGE_OPTIONS[0];
|
|
15265
15330
|
}
|
|
15266
15331
|
|
|
15332
|
+
const CACHE_KEY_PREFIX = "i18n.cache.";
|
|
15333
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
15334
|
+
function getCacheKey(language) {
|
|
15335
|
+
return `${CACHE_KEY_PREFIX}${language}`;
|
|
15336
|
+
}
|
|
15337
|
+
function buildUrl(baseUrl, language, version) {
|
|
15338
|
+
const normalized = baseUrl.replace(/\/+$/, "");
|
|
15339
|
+
const url = `${normalized}/${language}.json`;
|
|
15340
|
+
return version ? `${url}?v=${encodeURIComponent(version)}` : url;
|
|
15341
|
+
}
|
|
15342
|
+
/** True only for a plain object whose every value is a string. */
|
|
15343
|
+
function isFlatTranslations(value) {
|
|
15344
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
15345
|
+
return false;
|
|
15346
|
+
}
|
|
15347
|
+
return Object.values(value).every((entry) => typeof entry === "string");
|
|
15348
|
+
}
|
|
15349
|
+
function resolveFetch(options) {
|
|
15350
|
+
if (options.fetchImpl) {
|
|
15351
|
+
return options.fetchImpl;
|
|
15352
|
+
}
|
|
15353
|
+
return typeof fetch === "undefined" ? undefined : fetch;
|
|
15354
|
+
}
|
|
15355
|
+
function withTimeout(timeoutMs) {
|
|
15356
|
+
if (typeof AbortController === "undefined") {
|
|
15357
|
+
return { signal: undefined, done: () => undefined };
|
|
15358
|
+
}
|
|
15359
|
+
const controller = new AbortController();
|
|
15360
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
15361
|
+
return { signal: controller.signal, done: () => clearTimeout(timer) };
|
|
15362
|
+
}
|
|
15363
|
+
/**
|
|
15364
|
+
* Reads the cached translations for a language from localStorage. Returns
|
|
15365
|
+
* `null` when there is no cache, it is unreadable, or its shape is invalid.
|
|
15366
|
+
* Safe to call during SSR (returns `null` when localStorage is unavailable).
|
|
15367
|
+
*/
|
|
15368
|
+
function readCachedTranslations(language) {
|
|
15369
|
+
if (typeof localStorage === "undefined") {
|
|
15370
|
+
return null;
|
|
15371
|
+
}
|
|
15372
|
+
try {
|
|
15373
|
+
const raw = localStorage.getItem(getCacheKey(language));
|
|
15374
|
+
if (!raw) {
|
|
15375
|
+
return null;
|
|
15376
|
+
}
|
|
15377
|
+
const parsed = JSON.parse(raw);
|
|
15378
|
+
if (!isFlatTranslations(parsed.data)) {
|
|
15379
|
+
return null;
|
|
15380
|
+
}
|
|
15381
|
+
return {
|
|
15382
|
+
version: typeof parsed.version === "string" ? parsed.version : null,
|
|
15383
|
+
data: parsed.data,
|
|
15384
|
+
};
|
|
15385
|
+
}
|
|
15386
|
+
catch {
|
|
15387
|
+
return null;
|
|
15388
|
+
}
|
|
15389
|
+
}
|
|
15390
|
+
function writeCachedTranslations(language, payload) {
|
|
15391
|
+
if (typeof localStorage === "undefined") {
|
|
15392
|
+
return;
|
|
15393
|
+
}
|
|
15394
|
+
try {
|
|
15395
|
+
localStorage.setItem(getCacheKey(language), JSON.stringify(payload));
|
|
15396
|
+
}
|
|
15397
|
+
catch {
|
|
15398
|
+
// Quota exceeded or storage disabled: ignore, the in-memory result still applies.
|
|
15399
|
+
}
|
|
15400
|
+
}
|
|
15401
|
+
/**
|
|
15402
|
+
* Fetches the remote translations for a language, updating the localStorage
|
|
15403
|
+
* cache.
|
|
15404
|
+
*
|
|
15405
|
+
* Uses a plain GET (no custom request headers) on purpose: this keeps it a
|
|
15406
|
+
* "simple" CORS request, so the only server requirement is an
|
|
15407
|
+
* `Access-Control-Allow-Origin` header — no OPTIONS preflight. Freshness is
|
|
15408
|
+
* delegated to the HTTP layer via `cache: "no-cache"`, which makes the browser
|
|
15409
|
+
* revalidate against the CDN's `ETag`/`Last-Modified` transparently (a 304 is
|
|
15410
|
+
* resolved by the browser and surfaces here as a normal 200).
|
|
15411
|
+
*
|
|
15412
|
+
* Resolution order on failure: cached data (if any), otherwise `null` so the
|
|
15413
|
+
* caller can fall back to the embedded catalogs. Never throws.
|
|
15414
|
+
*/
|
|
15415
|
+
async function loadRemoteTranslations(language, options) {
|
|
15416
|
+
const fetchImpl = resolveFetch(options);
|
|
15417
|
+
if (!fetchImpl || !options.baseUrl) {
|
|
15418
|
+
return null;
|
|
15419
|
+
}
|
|
15420
|
+
const cached = readCachedTranslations(language);
|
|
15421
|
+
const url = buildUrl(options.baseUrl, language, options.version);
|
|
15422
|
+
const { signal, done } = withTimeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
15423
|
+
try {
|
|
15424
|
+
const response = await fetchImpl(url, {
|
|
15425
|
+
cache: "no-cache",
|
|
15426
|
+
signal,
|
|
15427
|
+
});
|
|
15428
|
+
if (!response.ok) {
|
|
15429
|
+
throw new Error(`Failed to load translations: HTTP ${response.status}`);
|
|
15430
|
+
}
|
|
15431
|
+
const data = await response.json();
|
|
15432
|
+
if (!isFlatTranslations(data)) {
|
|
15433
|
+
throw new Error("Remote translations payload is not a flat string map");
|
|
15434
|
+
}
|
|
15435
|
+
writeCachedTranslations(language, {
|
|
15436
|
+
version: options.version ?? null,
|
|
15437
|
+
fetchedAt: Date.now(),
|
|
15438
|
+
data,
|
|
15439
|
+
});
|
|
15440
|
+
return data;
|
|
15441
|
+
}
|
|
15442
|
+
catch (error) {
|
|
15443
|
+
options.onError?.(error);
|
|
15444
|
+
return cached?.data ?? null;
|
|
15445
|
+
}
|
|
15446
|
+
finally {
|
|
15447
|
+
done();
|
|
15448
|
+
}
|
|
15449
|
+
}
|
|
15450
|
+
|
|
15267
15451
|
function getStoredAppLanguage() {
|
|
15268
15452
|
if (typeof window === "undefined") {
|
|
15269
15453
|
return DEFAULT_APP_LANGUAGE;
|
|
@@ -15300,6 +15484,64 @@ function initializeAppLanguage(options = {}) {
|
|
|
15300
15484
|
}
|
|
15301
15485
|
return language;
|
|
15302
15486
|
}
|
|
15487
|
+
/**
|
|
15488
|
+
* Merges the embedded (build-time) catalog for a language with an optional
|
|
15489
|
+
* remote map and hands the result to `@angular/localize`. The remote entries
|
|
15490
|
+
* override the embedded ones, while any key missing remotely still resolves
|
|
15491
|
+
* from the embedded fallback — so partial remote files are safe.
|
|
15492
|
+
*/
|
|
15493
|
+
function applyTranslations(language, remote) {
|
|
15494
|
+
const merged = {
|
|
15495
|
+
...(ANGULAR_RUNTIME_TRANSLATIONS[language] ?? {}),
|
|
15496
|
+
...(remote ?? {}),
|
|
15497
|
+
};
|
|
15498
|
+
if (Object.keys(merged).length > 0) {
|
|
15499
|
+
loadTranslations(merged);
|
|
15500
|
+
}
|
|
15501
|
+
}
|
|
15502
|
+
/**
|
|
15503
|
+
* Initializes translations by loading them from the remote (AWS) JSON files,
|
|
15504
|
+
* with the embedded catalogs as a fallback. MUST be awaited before the host
|
|
15505
|
+
* application bootstraps, because `$localize` resolves messages at evaluation
|
|
15506
|
+
* time from the global map populated here.
|
|
15507
|
+
*
|
|
15508
|
+
* Behaviour:
|
|
15509
|
+
* - With a local cache: applies the cached copy immediately (instant boot) and
|
|
15510
|
+
* revalidates in the background; an updated copy takes effect on the next reload.
|
|
15511
|
+
* - Without a cache: awaits the remote fetch and, on any failure, falls back to
|
|
15512
|
+
* the embedded catalogs (never the raw keys).
|
|
15513
|
+
*/
|
|
15514
|
+
async function initializeAppLanguageAsync(options) {
|
|
15515
|
+
const language = getStoredAppLanguage();
|
|
15516
|
+
if (options.clearExistingTranslations) {
|
|
15517
|
+
clearTranslations();
|
|
15518
|
+
}
|
|
15519
|
+
applyDocumentLanguage(language);
|
|
15520
|
+
const cached = readCachedTranslations(language);
|
|
15521
|
+
if (cached) {
|
|
15522
|
+
applyTranslations(language, cached.data);
|
|
15523
|
+
// Fire-and-forget revalidation; the refreshed cache applies on next reload.
|
|
15524
|
+
void loadRemoteTranslations(language, options).catch(() => undefined);
|
|
15525
|
+
return language;
|
|
15526
|
+
}
|
|
15527
|
+
const remote = await loadRemoteTranslations(language, options);
|
|
15528
|
+
applyTranslations(language, remote);
|
|
15529
|
+
return language;
|
|
15530
|
+
}
|
|
15531
|
+
/**
|
|
15532
|
+
* Initializes translations from data the caller already fetched. Intended for
|
|
15533
|
+
* micro-frontend setups where a host fetches the remote translations once and
|
|
15534
|
+
* shares them with each MFE, avoiding one network request per MFE. The data is
|
|
15535
|
+
* merged over the embedded fallback, like {@link initializeAppLanguageAsync}.
|
|
15536
|
+
*/
|
|
15537
|
+
function initializeAppLanguageFromData(language, remote, options = {}) {
|
|
15538
|
+
if (options.clearExistingTranslations) {
|
|
15539
|
+
clearTranslations();
|
|
15540
|
+
}
|
|
15541
|
+
applyDocumentLanguage(language);
|
|
15542
|
+
applyTranslations(language, remote);
|
|
15543
|
+
return language;
|
|
15544
|
+
}
|
|
15303
15545
|
|
|
15304
15546
|
const PAGINATOR_COPY_BY_LANGUAGE = {
|
|
15305
15547
|
"pt-BR": {
|
|
@@ -15564,5 +15806,5 @@ function getMapaUiTexts(language) {
|
|
|
15564
15806
|
* Generated bundle index. Do not edit.
|
|
15565
15807
|
*/
|
|
15566
15808
|
|
|
15567
|
-
export { ANGULAR_RUNTIME_TRANSLATIONS, APP_LANGUAGE_OPTIONS, APP_LANGUAGE_STORAGE_KEY, DEFAULT_APP_LANGUAGE, MAPA_UI_TEXTS_PT_BR, MA_TRANSLATIONS, MCA_TRANSLATIONS, MENU_TRANSLATIONS, MF_TRANSLATIONS, MGA_TRANSLATIONS, MGC_TRANSLATIONS, MO_TRANSLATIONS, MP_TRANSLATIONS, MV_TRANSLATIONS, applyDocumentLanguage, applyPaginatorIntl, formatPaginatorRange, formatPaginatorShowingRange, getAngularLocale, getAppLanguageOption, getIntlLocale, getMapaUiTexts, getPaginatorNavigationLabels, getStoredAppLanguage, initializeAppLanguage, isAppLanguage, persistAppLanguage, resolveAppLanguage };
|
|
15809
|
+
export { ANGULAR_RUNTIME_TRANSLATIONS, APP_LANGUAGE_OPTIONS, APP_LANGUAGE_STORAGE_KEY, DEFAULT_APP_LANGUAGE, MAPA_UI_TEXTS_PT_BR, MA_TRANSLATIONS, MCA_TRANSLATIONS, MENU_TRANSLATIONS, MF_TRANSLATIONS, MGA_TRANSLATIONS, MGC_TRANSLATIONS, MO_TRANSLATIONS, MP_TRANSLATIONS, MV_TRANSLATIONS, applyDocumentLanguage, applyPaginatorIntl, formatPaginatorRange, formatPaginatorShowingRange, getAngularLocale, getAppLanguageOption, getIntlLocale, getMapaUiTexts, getPaginatorNavigationLabels, getStoredAppLanguage, initializeAppLanguage, initializeAppLanguageAsync, initializeAppLanguageFromData, isAppLanguage, loadRemoteTranslations, persistAppLanguage, readCachedTranslations, resolveAppLanguage };
|
|
15568
15810
|
//# sourceMappingURL=mapa-frontend-i18n.mjs.map
|