astro-intl 1.1.1 → 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 +162 -12
- package/dist/__tests__/framework-base.test.d.ts +1 -0
- package/dist/__tests__/framework-base.test.js +67 -0
- package/dist/__tests__/integration.test.js +2 -2
- package/dist/__tests__/react.test.js +26 -18
- package/dist/__tests__/svelte.test.d.ts +1 -0
- package/dist/__tests__/svelte.test.js +180 -0
- package/dist/adapters/react.d.ts +8 -0
- package/dist/adapters/react.js +45 -0
- package/dist/adapters/svelte.d.ts +14 -0
- package/dist/adapters/svelte.js +44 -0
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/framework-base.d.ts +11 -0
- package/dist/framework-base.js +24 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/translations.d.ts +0 -3
- package/dist/translations.js +1 -9
- package/package.json +15 -4
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@ Sistema de internacionalización simple y type-safe para Astro, inspirado en nex
|
|
|
6
6
|
|
|
7
7
|
- 🔒 **Type-safe**: Autocompletado y validación de claves de traducción con TypeScript
|
|
8
8
|
- 🎯 **API simple**: Inspirada en next-intl, fácil de usar
|
|
9
|
-
- ⚛️ **Soporte React**:
|
|
9
|
+
- ⚛️ **Soporte React**: Adapter dedicado con `t.rich()` para rich text con componentes React. Importa desde `astro-intl/react`
|
|
10
|
+
- 🧡 **Soporte Svelte**: Adapter dedicado con `t.rich()` que retorna segmentos y componente `RichText`. Importa desde `astro-intl/svelte`
|
|
10
11
|
- 🎨 **Markup en traducciones**: Inserta HTML en strings con `t.markup()`
|
|
11
12
|
- 📁 **Namespaces**: Organiza traducciones por secciones
|
|
12
13
|
- 🌐 **Detección automática de locale**: Extrae el idioma desde la URL
|
|
@@ -16,8 +17,35 @@ Sistema de internacionalización simple y type-safe para Astro, inspirado en nex
|
|
|
16
17
|
- 🗺️ **Routing localizado**: Define URLs traducidas por locale (`/es/sobre-nosotros` en vez de `/es/about`)
|
|
17
18
|
- 🔄 **Rewrites automáticos**: El middleware reescribe URLs traducidas a rutas canónicas del filesystem
|
|
18
19
|
- 🔗 **Generación de URLs**: `path()` y `switchLocalePath()` para construir y transformar URLs localizadas
|
|
20
|
+
- 📦 **Sub-path imports**: `astro-intl/react`, `astro-intl/svelte`, `astro-intl/routing`, `astro-intl/middleware`
|
|
19
21
|
|
|
20
|
-
##
|
|
22
|
+
## � Migración desde v1 a v2
|
|
23
|
+
|
|
24
|
+
### Breaking changes
|
|
25
|
+
|
|
26
|
+
1. **`getTranslationsReact` ya no se exporta desde `astro-intl`**. Usa `getTranslations` desde `astro-intl/react`:
|
|
27
|
+
|
|
28
|
+
```diff
|
|
29
|
+
- import { getTranslationsReact } from "astro-intl";
|
|
30
|
+
+ import { getTranslations } from "astro-intl/react";
|
|
31
|
+
|
|
32
|
+
- const t = getTranslationsReact();
|
|
33
|
+
+ const t = getTranslations();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
2. **Sub-path imports obligatorios para adapters de framework**:
|
|
37
|
+
- React: `astro-intl/react`
|
|
38
|
+
- Svelte: `astro-intl/svelte`
|
|
39
|
+
|
|
40
|
+
3. Las funciones base de Astro (`getTranslations`, `setRequestLocale`, `getLocale`, etc.) siguen exportándose desde `astro-intl` sin cambios.
|
|
41
|
+
|
|
42
|
+
### Nuevas funcionalidades
|
|
43
|
+
|
|
44
|
+
- **Adapter Svelte** con `t.rich()` y `renderRichText()`
|
|
45
|
+
- **`createGetTranslations` factory** en ambos adapters (React y Svelte) para uso standalone sin store global
|
|
46
|
+
- **`parseRichSegments()`** base agnóstica compartida
|
|
47
|
+
|
|
48
|
+
## �📦 Instalación
|
|
21
49
|
|
|
22
50
|
### Instalación automática (Recomendado)
|
|
23
51
|
|
|
@@ -179,11 +207,13 @@ const t = getTranslations();
|
|
|
179
207
|
|
|
180
208
|
### En componentes React
|
|
181
209
|
|
|
210
|
+
> **v2**: Importa desde `astro-intl/react` en lugar de `astro-intl`.
|
|
211
|
+
|
|
182
212
|
```tsx
|
|
183
|
-
import {
|
|
213
|
+
import { getTranslations } from "astro-intl/react";
|
|
184
214
|
|
|
185
215
|
export function MyComponent() {
|
|
186
|
-
const t =
|
|
216
|
+
const t = getTranslations();
|
|
187
217
|
|
|
188
218
|
return (
|
|
189
219
|
<div>
|
|
@@ -196,13 +226,29 @@ export function MyComponent() {
|
|
|
196
226
|
}
|
|
197
227
|
```
|
|
198
228
|
|
|
229
|
+
#### Factory standalone (sin store)
|
|
230
|
+
|
|
231
|
+
Si prefieres pasar los mensajes directamente sin depender del store global:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { createGetTranslations } from "astro-intl/react";
|
|
235
|
+
import { ui } from "../i18n";
|
|
236
|
+
|
|
237
|
+
const getT = createGetTranslations(ui, "en");
|
|
238
|
+
|
|
239
|
+
export function MyComponent({ lang }: { lang: string }) {
|
|
240
|
+
const t = getT(lang, "nav");
|
|
241
|
+
return <a href="/">{t("home")}</a>;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
199
245
|
### Traducciones con componentes React (rich text)
|
|
200
246
|
|
|
201
247
|
```tsx
|
|
202
|
-
import {
|
|
248
|
+
import { getTranslations } from "astro-intl/react";
|
|
203
249
|
|
|
204
250
|
export function MyComponent() {
|
|
205
|
-
const t =
|
|
251
|
+
const t = getTranslations();
|
|
206
252
|
|
|
207
253
|
// src/i18n/es.json
|
|
208
254
|
// { "terms": "Acepto los <link>términos y condiciones</link>" }
|
|
@@ -217,6 +263,78 @@ export function MyComponent() {
|
|
|
217
263
|
}
|
|
218
264
|
```
|
|
219
265
|
|
|
266
|
+
### En componentes Svelte
|
|
267
|
+
|
|
268
|
+
> **v2**: Nuevo adapter. Importa desde `astro-intl/svelte`.
|
|
269
|
+
|
|
270
|
+
```svelte
|
|
271
|
+
<script>
|
|
272
|
+
import { getTranslations } from 'astro-intl/svelte';
|
|
273
|
+
|
|
274
|
+
const t = getTranslations();
|
|
275
|
+
</script>
|
|
276
|
+
|
|
277
|
+
<h1>{t('welcome')}</h1>
|
|
278
|
+
<nav>
|
|
279
|
+
<a href="/">{t('nav.home')}</a>
|
|
280
|
+
</nav>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### Rich text en Svelte
|
|
284
|
+
|
|
285
|
+
`t.rich()` retorna un array de `RichSegment[]` que puedes renderizar con `renderRichText()`:
|
|
286
|
+
|
|
287
|
+
```svelte
|
|
288
|
+
<script>
|
|
289
|
+
import { getTranslations, renderRichText } from 'astro-intl/svelte';
|
|
290
|
+
|
|
291
|
+
// { "terms": "Acepto los <link>términos y condiciones</link>" }
|
|
292
|
+
const t = getTranslations();
|
|
293
|
+
const segments = t.rich('terms', ['link']);
|
|
294
|
+
|
|
295
|
+
const html = renderRichText(segments, {
|
|
296
|
+
tags: { link: 'a' }, // renderiza como <a>...</a>
|
|
297
|
+
});
|
|
298
|
+
</script>
|
|
299
|
+
|
|
300
|
+
<p>{@html html}</p>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
También puedes usar funciones personalizadas con `components`:
|
|
304
|
+
|
|
305
|
+
```svelte
|
|
306
|
+
<script>
|
|
307
|
+
import { getTranslations, renderRichText } from 'astro-intl/svelte';
|
|
308
|
+
|
|
309
|
+
const t = getTranslations();
|
|
310
|
+
const segments = t.rich('terms', ['link']);
|
|
311
|
+
|
|
312
|
+
const html = renderRichText(segments, {
|
|
313
|
+
components: {
|
|
314
|
+
link: (chunks) => `<a href="/terms" class="underline">${chunks}</a>`,
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
</script>
|
|
318
|
+
|
|
319
|
+
<p>{@html html}</p>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
#### Factory standalone en Svelte (sin store)
|
|
323
|
+
|
|
324
|
+
```svelte
|
|
325
|
+
<script>
|
|
326
|
+
import { createGetTranslations } from 'astro-intl/svelte';
|
|
327
|
+
import { ui } from '../i18n';
|
|
328
|
+
|
|
329
|
+
const getT = createGetTranslations(ui, 'en');
|
|
330
|
+
|
|
331
|
+
export let lang;
|
|
332
|
+
const t = getT(lang, 'nav');
|
|
333
|
+
</script>
|
|
334
|
+
|
|
335
|
+
<a href="/">{t('home')}</a>
|
|
336
|
+
```
|
|
337
|
+
|
|
220
338
|
### Type-safety con TypeScript
|
|
221
339
|
|
|
222
340
|
```astro
|
|
@@ -431,15 +549,44 @@ Obtiene la función de traducción para componentes Astro.
|
|
|
431
549
|
- `Record<string, (chunks: string) => string>` - Solo tags (backward compatible)
|
|
432
550
|
- `{ values?: Record<string, Primitive>, tags: Record<string, (chunks: string) => string> }` - Tags con interpolación
|
|
433
551
|
|
|
434
|
-
### `
|
|
552
|
+
### `getTranslations()` — `astro-intl/react`
|
|
553
|
+
|
|
554
|
+
Obtiene la función de traducción para componentes React (usa el store global).
|
|
555
|
+
|
|
556
|
+
**Retorna:** Función `t(key)` con método `t.rich(key, tags)` que retorna `ReactNode[]`
|
|
557
|
+
|
|
558
|
+
### `createGetTranslations(ui, defaultLocale)` — `astro-intl/react`
|
|
559
|
+
|
|
560
|
+
Factory standalone que no depende del store global. Útil para pasar mensajes directamente.
|
|
561
|
+
|
|
562
|
+
**Parámetros:**
|
|
563
|
+
|
|
564
|
+
- `ui: Record<string, Record<string, unknown>>` - Objeto con todos los mensajes por locale
|
|
565
|
+
- `defaultLocale: string` - Locale por defecto
|
|
566
|
+
|
|
567
|
+
**Retorna:** `(lang, namespace) => t` — función que retorna `t(key)` con `t.rich(key, tags)`
|
|
568
|
+
|
|
569
|
+
### `getTranslations()` — `astro-intl/svelte`
|
|
570
|
+
|
|
571
|
+
Obtiene la función de traducción para componentes Svelte (usa el store global).
|
|
572
|
+
|
|
573
|
+
**Retorna:** Función `t(key)` con método `t.rich(key, tagNames?)` que retorna `RichSegment[]`
|
|
574
|
+
|
|
575
|
+
### `createGetTranslations(ui, defaultLocale)` — `astro-intl/svelte`
|
|
576
|
+
|
|
577
|
+
Factory standalone para Svelte. Misma firma que el de React pero `t.rich()` retorna `RichSegment[]`.
|
|
578
|
+
|
|
579
|
+
### `renderRichText(segments, options?)` — `astro-intl/svelte`
|
|
435
580
|
|
|
436
|
-
|
|
581
|
+
Resuelve un array de `RichSegment[]` en un string HTML.
|
|
437
582
|
|
|
438
583
|
**Parámetros:**
|
|
439
584
|
|
|
440
|
-
- `
|
|
585
|
+
- `segments: RichSegment[]` - Segmentos retornados por `t.rich()`
|
|
586
|
+
- `options.tags?: Record<string, string>` - Mapea nombre de tag a elemento HTML (ej: `{ link: 'a' }`)
|
|
587
|
+
- `options.components?: Record<string, (chunks: string) => string>` - Funciones personalizadas por tag
|
|
441
588
|
|
|
442
|
-
**Retorna:**
|
|
589
|
+
**Retorna:** `string` - HTML listo para renderizar con `{@html}`
|
|
443
590
|
|
|
444
591
|
### `getLocale()`
|
|
445
592
|
|
|
@@ -518,12 +665,15 @@ Esto actualizará los enlaces simbólicos y los tipos estarán disponibles en lo
|
|
|
518
665
|
```text
|
|
519
666
|
packages/integration/
|
|
520
667
|
├── src/
|
|
668
|
+
│ ├── adapters/
|
|
669
|
+
│ │ ├── react.ts # Adapter React — getTranslations, createGetTranslations, t.rich() → ReactNode[]
|
|
670
|
+
│ │ └── svelte.ts # Adapter Svelte — getTranslations, createGetTranslations, t.rich() → RichSegment[], renderRichText()
|
|
521
671
|
│ ├── core.ts # Barrel — re-exporta todo desde los módulos
|
|
672
|
+
│ ├── framework-base.ts # parseRichSegments() — base agnóstica compartida por React y Svelte
|
|
522
673
|
│ ├── sanitize.ts # Validación de locale, sanitización HTML, escape regex
|
|
523
674
|
│ ├── interpolation.ts # Interpolación {variables}, acceso a valores anidados
|
|
524
675
|
│ ├── store.ts # Estado por request (AsyncLocalStorage + fallback)
|
|
525
|
-
│ ├── translations.ts # getTranslations
|
|
526
|
-
│ ├── react.ts # Factory de t.rich() para React
|
|
676
|
+
│ ├── translations.ts # getTranslations para componentes Astro
|
|
527
677
|
│ ├── routing.ts # path(), switchLocalePath() — generación de URLs localizadas
|
|
528
678
|
│ ├── middleware.ts # createIntlMiddleware() con rewrites de rutas traducidas
|
|
529
679
|
│ ├── index.ts # Entry point público + integración de Astro
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseRichSegments } from "../framework-base.js";
|
|
3
|
+
describe("framework-base.ts", () => {
|
|
4
|
+
describe("parseRichSegments", () => {
|
|
5
|
+
it("should return single text segment for plain text", () => {
|
|
6
|
+
const result = parseRichSegments("Hello world", []);
|
|
7
|
+
expect(result).toEqual([{ type: "text", value: "Hello world" }]);
|
|
8
|
+
});
|
|
9
|
+
it("should return empty array for empty string", () => {
|
|
10
|
+
const result = parseRichSegments("", []);
|
|
11
|
+
expect(result).toEqual([]);
|
|
12
|
+
});
|
|
13
|
+
it("should parse a single tag", () => {
|
|
14
|
+
const result = parseRichSegments("Click <link>here</link> to continue", ["link"]);
|
|
15
|
+
expect(result).toEqual([
|
|
16
|
+
{ type: "text", value: "Click " },
|
|
17
|
+
{ type: "tag", tag: "link", chunks: "here" },
|
|
18
|
+
{ type: "text", value: " to continue" },
|
|
19
|
+
]);
|
|
20
|
+
});
|
|
21
|
+
it("should parse multiple tags", () => {
|
|
22
|
+
const result = parseRichSegments("Text with <bold>bold</bold> and <italic>italic</italic>", ["bold", "italic"]);
|
|
23
|
+
expect(result).toEqual([
|
|
24
|
+
{ type: "text", value: "Text with " },
|
|
25
|
+
{ type: "tag", tag: "bold", chunks: "bold" },
|
|
26
|
+
{ type: "text", value: " and " },
|
|
27
|
+
{ type: "tag", tag: "italic", chunks: "italic" },
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
it("should handle adjacent tags", () => {
|
|
31
|
+
const result = parseRichSegments("<tag1>First</tag1><tag2>Second</tag2>", [
|
|
32
|
+
"tag1",
|
|
33
|
+
"tag2",
|
|
34
|
+
]);
|
|
35
|
+
expect(result).toEqual([
|
|
36
|
+
{ type: "tag", tag: "tag1", chunks: "First" },
|
|
37
|
+
{ type: "tag", tag: "tag2", chunks: "Second" },
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
it("should handle empty chunks", () => {
|
|
41
|
+
const result = parseRichSegments("Text <tag></tag> more text", ["tag"]);
|
|
42
|
+
expect(result).toEqual([
|
|
43
|
+
{ type: "text", value: "Text " },
|
|
44
|
+
{ type: "tag", tag: "tag", chunks: "" },
|
|
45
|
+
{ type: "text", value: " more text" },
|
|
46
|
+
]);
|
|
47
|
+
});
|
|
48
|
+
it("should return text segment when no matching tags found", () => {
|
|
49
|
+
const result = parseRichSegments("Hello <unknown>world</unknown>", ["link"]);
|
|
50
|
+
expect(result).toEqual([
|
|
51
|
+
{ type: "text", value: "Hello <unknown>world</unknown>" },
|
|
52
|
+
]);
|
|
53
|
+
});
|
|
54
|
+
it("should handle tag names with special regex characters", () => {
|
|
55
|
+
const result = parseRichSegments("Click <link.ext>here</link.ext> now", ["link.ext"]);
|
|
56
|
+
expect(result).toEqual([
|
|
57
|
+
{ type: "text", value: "Click " },
|
|
58
|
+
{ type: "tag", tag: "link.ext", chunks: "here" },
|
|
59
|
+
{ type: "text", value: " now" },
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
it("should treat text as plain when tagNames is empty", () => {
|
|
63
|
+
const result = parseRichSegments("Click <link>here</link>", []);
|
|
64
|
+
expect(result).toEqual([{ type: "text", value: "Click <link>here</link>" }]);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import { setRequestLocale, getLocale, getTranslations,
|
|
2
|
+
import { setRequestLocale, getLocale, getTranslations, __resetRequestConfig } from "../index.js";
|
|
3
|
+
import { getTranslations as getTranslationsReact } from "../adapters/react.js";
|
|
3
4
|
describe("Integration Tests", () => {
|
|
4
5
|
beforeEach(() => {
|
|
5
6
|
__resetRequestConfig();
|
|
@@ -127,7 +128,6 @@ describe("Integration Tests", () => {
|
|
|
127
128
|
it("should throw descriptive error when accessing translations before setup", () => {
|
|
128
129
|
expect(() => getLocale()).toThrow("[astro-intl] No request config found. Did you call setRequestLocale()?");
|
|
129
130
|
expect(() => getTranslations()).toThrow("[astro-intl] No request config found. Did you call setRequestLocale()?");
|
|
130
|
-
expect(() => getTranslationsReact()).toThrow("[astro-intl] No request config found. Did you call setRequestLocale()?");
|
|
131
131
|
});
|
|
132
132
|
it("should handle missing translations gracefully", async () => {
|
|
133
133
|
const url = new URL("https://example.com/en/page");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import { createGetTranslations } from "../adapters/react.js";
|
|
3
3
|
describe("react.ts", () => {
|
|
4
4
|
const ui = {
|
|
5
5
|
en: {
|
|
@@ -31,36 +31,36 @@ describe("react.ts", () => {
|
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
};
|
|
34
|
-
describe("
|
|
34
|
+
describe("createGetTranslations", () => {
|
|
35
35
|
it("should create translation function for default locale", () => {
|
|
36
|
-
const getT =
|
|
36
|
+
const getT = createGetTranslations(ui, "en");
|
|
37
37
|
const t = getT("en", "common");
|
|
38
38
|
expect(t("greeting")).toBe("Hello");
|
|
39
39
|
expect(t("farewell")).toBe("Goodbye");
|
|
40
40
|
});
|
|
41
41
|
it("should create translation function for non-default locale", () => {
|
|
42
|
-
const getT =
|
|
42
|
+
const getT = createGetTranslations(ui, "en");
|
|
43
43
|
const t = getT("es", "common");
|
|
44
44
|
expect(t("greeting")).toBe("Hola");
|
|
45
45
|
expect(t("farewell")).toBe("Adiós");
|
|
46
46
|
});
|
|
47
47
|
it("should handle nested keys", () => {
|
|
48
|
-
const getT =
|
|
48
|
+
const getT = createGetTranslations(ui, "en");
|
|
49
49
|
const t = getT("en", "common");
|
|
50
50
|
expect(t("nested.deep")).toBe("Deep value");
|
|
51
51
|
});
|
|
52
52
|
it("should return key when translation not found", () => {
|
|
53
|
-
const getT =
|
|
53
|
+
const getT = createGetTranslations(ui, "en");
|
|
54
54
|
const t = getT("en", "common");
|
|
55
55
|
expect(t("nonexistent")).toBe("nonexistent");
|
|
56
56
|
});
|
|
57
57
|
it("should fallback to default locale when locale not found", () => {
|
|
58
|
-
const getT =
|
|
58
|
+
const getT = createGetTranslations(ui, "en");
|
|
59
59
|
const t = getT("fr", "common");
|
|
60
60
|
expect(t("greeting")).toBe("Hello");
|
|
61
61
|
});
|
|
62
62
|
it("should work with different namespaces", () => {
|
|
63
|
-
const getT =
|
|
63
|
+
const getT = createGetTranslations(ui, "en");
|
|
64
64
|
const tCommon = getT("en", "common");
|
|
65
65
|
const tHome = getT("en", "home");
|
|
66
66
|
expect(tCommon("greeting")).toBe("Hello");
|
|
@@ -69,7 +69,7 @@ describe("react.ts", () => {
|
|
|
69
69
|
});
|
|
70
70
|
describe("t.rich", () => {
|
|
71
71
|
it("should interpolate React components", () => {
|
|
72
|
-
const getT =
|
|
72
|
+
const getT = createGetTranslations(ui, "en");
|
|
73
73
|
const t = getT("en", "home");
|
|
74
74
|
const result = t.rich("withTags", {
|
|
75
75
|
link: (chunks) => `<a>${chunks}</a>`,
|
|
@@ -77,7 +77,7 @@ describe("react.ts", () => {
|
|
|
77
77
|
expect(result).toEqual(["Click ", "<a>here</a>", " to continue"]);
|
|
78
78
|
});
|
|
79
79
|
it("should handle multiple tags", () => {
|
|
80
|
-
const getT =
|
|
80
|
+
const getT = createGetTranslations(ui, "en");
|
|
81
81
|
const t = getT("en", "home");
|
|
82
82
|
const result = t.rich("multipleTags", {
|
|
83
83
|
bold: (chunks) => `<strong>${chunks}</strong>`,
|
|
@@ -86,13 +86,13 @@ describe("react.ts", () => {
|
|
|
86
86
|
expect(result).toEqual(["Text with ", "<strong>bold</strong>", " and ", "<em>italic</em>"]);
|
|
87
87
|
});
|
|
88
88
|
it("should handle text without tags", () => {
|
|
89
|
-
const getT =
|
|
89
|
+
const getT = createGetTranslations(ui, "en");
|
|
90
90
|
const t = getT("en", "common");
|
|
91
91
|
const result = t.rich("greeting", {});
|
|
92
92
|
expect(result).toEqual(["Hello"]);
|
|
93
93
|
});
|
|
94
94
|
it("should work with Spanish locale", () => {
|
|
95
|
-
const getT =
|
|
95
|
+
const getT = createGetTranslations(ui, "en");
|
|
96
96
|
const t = getT("es", "home");
|
|
97
97
|
const result = t.rich("withTags", {
|
|
98
98
|
link: (chunks) => `<a>${chunks}</a>`,
|
|
@@ -107,13 +107,21 @@ describe("react.ts", () => {
|
|
|
107
107
|
},
|
|
108
108
|
},
|
|
109
109
|
};
|
|
110
|
-
const getT =
|
|
110
|
+
const getT = createGetTranslations(customUi, "en");
|
|
111
111
|
const t = getT("en", "test");
|
|
112
112
|
const result = t.rich("nested", {
|
|
113
|
-
outer: (chunks) =>
|
|
114
|
-
inner: (chunks) =>
|
|
113
|
+
outer: (chunks) => ({ wrapper: "outer", children: chunks }),
|
|
114
|
+
inner: (chunks) => ({ wrapper: "inner", children: chunks }),
|
|
115
115
|
});
|
|
116
|
-
|
|
116
|
+
// outer receives a ReactNode[] because inner was processed recursively
|
|
117
|
+
expect(result).toEqual([
|
|
118
|
+
"Start ",
|
|
119
|
+
{
|
|
120
|
+
wrapper: "outer",
|
|
121
|
+
children: ["outer ", { wrapper: "inner", children: "inner" }, " outer"],
|
|
122
|
+
},
|
|
123
|
+
" end",
|
|
124
|
+
]);
|
|
117
125
|
});
|
|
118
126
|
it("should handle adjacent tags", () => {
|
|
119
127
|
const customUi = {
|
|
@@ -123,7 +131,7 @@ describe("react.ts", () => {
|
|
|
123
131
|
},
|
|
124
132
|
},
|
|
125
133
|
};
|
|
126
|
-
const getT =
|
|
134
|
+
const getT = createGetTranslations(customUi, "en");
|
|
127
135
|
const t = getT("en", "test");
|
|
128
136
|
const result = t.rich("adjacent", {
|
|
129
137
|
tag1: (chunks) => `[${chunks}]`,
|
|
@@ -139,7 +147,7 @@ describe("react.ts", () => {
|
|
|
139
147
|
},
|
|
140
148
|
},
|
|
141
149
|
};
|
|
142
|
-
const getT =
|
|
150
|
+
const getT = createGetTranslations(customUi, "en");
|
|
143
151
|
const t = getT("en", "test");
|
|
144
152
|
const result = t.rich("empty", {
|
|
145
153
|
tag: (chunks) => `[${chunks}]`,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { createGetTranslations, renderRichText } from "../adapters/svelte.js";
|
|
3
|
+
describe("svelte adapter", () => {
|
|
4
|
+
const ui = {
|
|
5
|
+
en: {
|
|
6
|
+
common: {
|
|
7
|
+
greeting: "Hello",
|
|
8
|
+
farewell: "Goodbye",
|
|
9
|
+
nested: {
|
|
10
|
+
deep: "Deep value",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
home: {
|
|
14
|
+
title: "Welcome Home",
|
|
15
|
+
withTags: "Click <link>here</link> to continue",
|
|
16
|
+
multipleTags: "Text with <bold>bold</bold> and <italic>italic</italic>",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
es: {
|
|
20
|
+
common: {
|
|
21
|
+
greeting: "Hola",
|
|
22
|
+
farewell: "Adiós",
|
|
23
|
+
nested: {
|
|
24
|
+
deep: "Valor profundo",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
home: {
|
|
28
|
+
title: "Bienvenido a Casa",
|
|
29
|
+
withTags: "Haz clic <link>aquí</link> para continuar",
|
|
30
|
+
multipleTags: "Texto con <bold>negrita</bold> y <italic>cursiva</italic>",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
describe("createGetTranslations", () => {
|
|
35
|
+
it("should create translation function for default locale", () => {
|
|
36
|
+
const getT = createGetTranslations(ui, "en");
|
|
37
|
+
const t = getT("en", "common");
|
|
38
|
+
expect(t("greeting")).toBe("Hello");
|
|
39
|
+
expect(t("farewell")).toBe("Goodbye");
|
|
40
|
+
});
|
|
41
|
+
it("should create translation function for non-default locale", () => {
|
|
42
|
+
const getT = createGetTranslations(ui, "en");
|
|
43
|
+
const t = getT("es", "common");
|
|
44
|
+
expect(t("greeting")).toBe("Hola");
|
|
45
|
+
expect(t("farewell")).toBe("Adiós");
|
|
46
|
+
});
|
|
47
|
+
it("should handle nested keys", () => {
|
|
48
|
+
const getT = createGetTranslations(ui, "en");
|
|
49
|
+
const t = getT("en", "common");
|
|
50
|
+
expect(t("nested.deep")).toBe("Deep value");
|
|
51
|
+
});
|
|
52
|
+
it("should return key when translation not found", () => {
|
|
53
|
+
const getT = createGetTranslations(ui, "en");
|
|
54
|
+
const t = getT("en", "common");
|
|
55
|
+
expect(t("nonexistent")).toBe("nonexistent");
|
|
56
|
+
});
|
|
57
|
+
it("should fallback to default locale when locale not found", () => {
|
|
58
|
+
const getT = createGetTranslations(ui, "en");
|
|
59
|
+
const t = getT("fr", "common");
|
|
60
|
+
expect(t("greeting")).toBe("Hello");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe("t.rich", () => {
|
|
64
|
+
it("should return segments for a single tag", () => {
|
|
65
|
+
const getT = createGetTranslations(ui, "en");
|
|
66
|
+
const t = getT("en", "home");
|
|
67
|
+
const result = t.rich("withTags", ["link"]);
|
|
68
|
+
expect(result).toEqual([
|
|
69
|
+
{ type: "text", value: "Click " },
|
|
70
|
+
{ type: "tag", tag: "link", chunks: "here" },
|
|
71
|
+
{ type: "text", value: " to continue" },
|
|
72
|
+
]);
|
|
73
|
+
});
|
|
74
|
+
it("should return segments for multiple tags", () => {
|
|
75
|
+
const getT = createGetTranslations(ui, "en");
|
|
76
|
+
const t = getT("en", "home");
|
|
77
|
+
const result = t.rich("multipleTags", ["bold", "italic"]);
|
|
78
|
+
expect(result).toEqual([
|
|
79
|
+
{ type: "text", value: "Text with " },
|
|
80
|
+
{ type: "tag", tag: "bold", chunks: "bold" },
|
|
81
|
+
{ type: "text", value: " and " },
|
|
82
|
+
{ type: "tag", tag: "italic", chunks: "italic" },
|
|
83
|
+
]);
|
|
84
|
+
});
|
|
85
|
+
it("should return text segment when no tagNames provided", () => {
|
|
86
|
+
const getT = createGetTranslations(ui, "en");
|
|
87
|
+
const t = getT("en", "common");
|
|
88
|
+
const result = t.rich("greeting");
|
|
89
|
+
expect(result).toEqual([{ type: "text", value: "Hello" }]);
|
|
90
|
+
});
|
|
91
|
+
it("should work with Spanish locale", () => {
|
|
92
|
+
const getT = createGetTranslations(ui, "en");
|
|
93
|
+
const t = getT("es", "home");
|
|
94
|
+
const result = t.rich("withTags", ["link"]);
|
|
95
|
+
expect(result).toEqual([
|
|
96
|
+
{ type: "text", value: "Haz clic " },
|
|
97
|
+
{ type: "tag", tag: "link", chunks: "aquí" },
|
|
98
|
+
{ type: "text", value: " para continuar" },
|
|
99
|
+
]);
|
|
100
|
+
});
|
|
101
|
+
it("should handle adjacent tags", () => {
|
|
102
|
+
const customUi = {
|
|
103
|
+
en: {
|
|
104
|
+
test: {
|
|
105
|
+
adjacent: "<tag1>First</tag1><tag2>Second</tag2>",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const getT = createGetTranslations(customUi, "en");
|
|
110
|
+
const t = getT("en", "test");
|
|
111
|
+
const result = t.rich("adjacent", ["tag1", "tag2"]);
|
|
112
|
+
expect(result).toEqual([
|
|
113
|
+
{ type: "tag", tag: "tag1", chunks: "First" },
|
|
114
|
+
{ type: "tag", tag: "tag2", chunks: "Second" },
|
|
115
|
+
]);
|
|
116
|
+
});
|
|
117
|
+
it("should handle empty chunks", () => {
|
|
118
|
+
const customUi = {
|
|
119
|
+
en: {
|
|
120
|
+
test: {
|
|
121
|
+
empty: "Text <tag></tag> more text",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
const getT = createGetTranslations(customUi, "en");
|
|
126
|
+
const t = getT("en", "test");
|
|
127
|
+
const result = t.rich("empty", ["tag"]);
|
|
128
|
+
expect(result).toEqual([
|
|
129
|
+
{ type: "text", value: "Text " },
|
|
130
|
+
{ type: "tag", tag: "tag", chunks: "" },
|
|
131
|
+
{ type: "text", value: " more text" },
|
|
132
|
+
]);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("renderRichText", () => {
|
|
136
|
+
it("should resolve tags to native HTML elements", () => {
|
|
137
|
+
const getT = createGetTranslations(ui, "en");
|
|
138
|
+
const t = getT("en", "home");
|
|
139
|
+
const segments = t.rich("multipleTags", ["bold", "italic"]);
|
|
140
|
+
const html = renderRichText(segments, {
|
|
141
|
+
tags: { bold: "strong", italic: "em" },
|
|
142
|
+
});
|
|
143
|
+
expect(html).toBe("Text with <strong>bold</strong> and <em>italic</em>");
|
|
144
|
+
});
|
|
145
|
+
it("should resolve components as callback functions", () => {
|
|
146
|
+
const getT = createGetTranslations(ui, "en");
|
|
147
|
+
const t = getT("en", "home");
|
|
148
|
+
const segments = t.rich("withTags", ["link"]);
|
|
149
|
+
const html = renderRichText(segments, {
|
|
150
|
+
components: {
|
|
151
|
+
link: (chunks) => `<a href="/target">${chunks}</a>`,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
expect(html).toBe('Click <a href="/target">here</a> to continue');
|
|
155
|
+
});
|
|
156
|
+
it("should mix tags and components", () => {
|
|
157
|
+
const getT = createGetTranslations(ui, "en");
|
|
158
|
+
const t = getT("en", "home");
|
|
159
|
+
const segments = t.rich("multipleTags", ["bold", "italic"]);
|
|
160
|
+
const html = renderRichText(segments, {
|
|
161
|
+
tags: { bold: "strong" },
|
|
162
|
+
components: {
|
|
163
|
+
italic: (chunks) => `<span class="italic">${chunks}</span>`,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
expect(html).toBe('Text with <strong>bold</strong> and <span class="italic">italic</span>');
|
|
167
|
+
});
|
|
168
|
+
it("should fallback to plain chunks when no resolver found", () => {
|
|
169
|
+
const getT = createGetTranslations(ui, "en");
|
|
170
|
+
const t = getT("en", "home");
|
|
171
|
+
const segments = t.rich("withTags", ["link"]);
|
|
172
|
+
const html = renderRichText(segments);
|
|
173
|
+
expect(html).toBe("Click here to continue");
|
|
174
|
+
});
|
|
175
|
+
it("should handle empty segments", () => {
|
|
176
|
+
const html = renderRichText([]);
|
|
177
|
+
expect(html).toBe("");
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type DotPaths } from "../interpolation.js";
|
|
3
|
+
export declare function createGetTranslations<UI extends Record<string, Record<string, unknown>>, DefaultLocale extends keyof UI>(ui: UI, defaultLocale: DefaultLocale): <N extends keyof UI[DefaultLocale]>(lang: string | undefined, namespace: N) => ((key: DotPaths<UI[DefaultLocale][N]>) => string) & {
|
|
4
|
+
rich: (key: DotPaths<UI[DefaultLocale][N]>, tags: Record<string, (chunks: ReactNode) => ReactNode>) => ReactNode[];
|
|
5
|
+
};
|
|
6
|
+
export declare function getTranslations<T extends Record<string, unknown> = Record<string, unknown>>(namespace?: string): ((key: DotPaths<T>) => string) & {
|
|
7
|
+
rich: (key: DotPaths<T>, tags: Record<string, (chunks: ReactNode) => ReactNode>) => ReactNode[];
|
|
8
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getNestedValue } from "../interpolation.js";
|
|
2
|
+
import { parseRichSegments } from "../framework-base.js";
|
|
3
|
+
import { getMessages, getLocale } from "../store.js";
|
|
4
|
+
// ─── createGetTranslations (standalone, no store dependency) ────────
|
|
5
|
+
export function createGetTranslations(ui, defaultLocale) {
|
|
6
|
+
return function getTranslations(lang, namespace) {
|
|
7
|
+
const resolvedLang = lang && lang in ui ? lang : defaultLocale;
|
|
8
|
+
const messages = ui[resolvedLang][namespace];
|
|
9
|
+
function t(key) {
|
|
10
|
+
const value = getNestedValue(messages, key);
|
|
11
|
+
return typeof value === "string" ? value : key;
|
|
12
|
+
}
|
|
13
|
+
const rich = function (key, tags) {
|
|
14
|
+
const str = t(key);
|
|
15
|
+
const tagNames = Object.keys(tags);
|
|
16
|
+
function processString(input) {
|
|
17
|
+
const segments = parseRichSegments(input, tagNames);
|
|
18
|
+
return segments.map((seg) => {
|
|
19
|
+
if (seg.type === "text")
|
|
20
|
+
return seg.value;
|
|
21
|
+
// Recursively process nested tags within chunks
|
|
22
|
+
const innerSegments = parseRichSegments(seg.chunks, tagNames);
|
|
23
|
+
const hasNestedTags = innerSegments.some((s) => s.type === "tag");
|
|
24
|
+
const resolvedChunks = hasNestedTags ? processString(seg.chunks) : seg.chunks;
|
|
25
|
+
const tagFn = tags[seg.tag];
|
|
26
|
+
if (typeof tagFn !== "function") {
|
|
27
|
+
console.warn(`[astro-intl] Unregistered rich tag: <${seg.tag}>. Content will render without transformation.`);
|
|
28
|
+
return resolvedChunks;
|
|
29
|
+
}
|
|
30
|
+
return tagFn(resolvedChunks);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return processString(str);
|
|
34
|
+
};
|
|
35
|
+
Object.assign(t, { rich });
|
|
36
|
+
return t;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// ─── getTranslations (store-backed, for use in Astro islands) ───────
|
|
40
|
+
export function getTranslations(namespace) {
|
|
41
|
+
const messages = getMessages(namespace);
|
|
42
|
+
const locale = getLocale();
|
|
43
|
+
const ui = { [locale]: { default: messages } };
|
|
44
|
+
return createGetTranslations(ui, locale)(locale, "default");
|
|
45
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type DotPaths } from "../interpolation.js";
|
|
2
|
+
import { type RichSegment } from "../framework-base.js";
|
|
3
|
+
export type { RichSegment } from "../framework-base.js";
|
|
4
|
+
export declare function createGetTranslations<UI extends Record<string, Record<string, unknown>>, DefaultLocale extends keyof UI>(ui: UI, defaultLocale: DefaultLocale): <N extends keyof UI[DefaultLocale]>(lang: string | undefined, namespace: N) => ((key: DotPaths<UI[DefaultLocale][N]>) => string) & {
|
|
5
|
+
rich: (key: DotPaths<UI[DefaultLocale][N]>, tagNames?: string[]) => RichSegment[];
|
|
6
|
+
};
|
|
7
|
+
export type RichTextOptions = {
|
|
8
|
+
tags?: Record<string, string>;
|
|
9
|
+
components?: Record<string, (chunks: string) => string>;
|
|
10
|
+
};
|
|
11
|
+
export declare function renderRichText(segments: RichSegment[], options?: RichTextOptions): string;
|
|
12
|
+
export declare function getTranslations<T extends Record<string, unknown> = Record<string, unknown>>(namespace?: string): ((key: DotPaths<T>) => string) & {
|
|
13
|
+
rich: (key: DotPaths<T>, tagNames?: string[]) => RichSegment[];
|
|
14
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getNestedValue } from "../interpolation.js";
|
|
2
|
+
import { parseRichSegments } from "../framework-base.js";
|
|
3
|
+
import { getMessages, getLocale } from "../store.js";
|
|
4
|
+
// ─── createGetTranslations (standalone, no store dependency) ────────
|
|
5
|
+
export function createGetTranslations(ui, defaultLocale) {
|
|
6
|
+
return function getTranslations(lang, namespace) {
|
|
7
|
+
const resolvedLang = lang && lang in ui ? lang : defaultLocale;
|
|
8
|
+
const messages = ui[resolvedLang][namespace];
|
|
9
|
+
function t(key) {
|
|
10
|
+
const value = getNestedValue(messages, key);
|
|
11
|
+
return typeof value === "string" ? value : key;
|
|
12
|
+
}
|
|
13
|
+
const rich = function (key, tagNames) {
|
|
14
|
+
const str = t(key);
|
|
15
|
+
return parseRichSegments(str, tagNames ?? []);
|
|
16
|
+
};
|
|
17
|
+
Object.assign(t, { rich });
|
|
18
|
+
return t;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function renderRichText(segments, options = {}) {
|
|
22
|
+
const { tags = {}, components = {} } = options;
|
|
23
|
+
return segments
|
|
24
|
+
.map((seg) => {
|
|
25
|
+
if (seg.type === "text")
|
|
26
|
+
return seg.value;
|
|
27
|
+
const componentFn = components[seg.tag];
|
|
28
|
+
if (typeof componentFn === "function")
|
|
29
|
+
return componentFn(seg.chunks);
|
|
30
|
+
const htmlTag = tags[seg.tag];
|
|
31
|
+
if (typeof htmlTag === "string")
|
|
32
|
+
return `<${htmlTag}>${seg.chunks}</${htmlTag}>`;
|
|
33
|
+
console.warn(`[astro-intl] Unregistered rich tag: <${seg.tag}>. Content will render without transformation.`);
|
|
34
|
+
return seg.chunks;
|
|
35
|
+
})
|
|
36
|
+
.join("");
|
|
37
|
+
}
|
|
38
|
+
// ─── getTranslations (store-backed, for use in Astro islands) ───────
|
|
39
|
+
export function getTranslations(namespace) {
|
|
40
|
+
const messages = getMessages(namespace);
|
|
41
|
+
const locale = getLocale();
|
|
42
|
+
const ui = { [locale]: { default: messages } };
|
|
43
|
+
return createGetTranslations(ui, locale)(locale, "default");
|
|
44
|
+
}
|
package/dist/core.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { setRequestLocale, runWithLocale, getLocale, getLocales, isValidLocale, getMessages, getDefaultLocale, defineRequestConfig, __setConfigMessages, __resetRequestConfig, __setIntlConfig, } from "./store.js";
|
|
2
|
-
export { getTranslations
|
|
2
|
+
export { getTranslations } from "./translations.js";
|
|
3
3
|
export { getNestedValue, type DotPaths } from "./interpolation.js";
|
|
4
4
|
export { sanitizeLocale, sanitizeHtml, escapeRegExp } from "./sanitize.js";
|
|
5
5
|
export { path, switchLocalePath, templateToRegex } from "./routing.js";
|
package/dist/core.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ─── Store (request lifecycle) ──────────────────────────────────────
|
|
2
2
|
export { setRequestLocale, runWithLocale, getLocale, getLocales, isValidLocale, getMessages, getDefaultLocale, defineRequestConfig, __setConfigMessages, __resetRequestConfig, __setIntlConfig, } from "./store.js";
|
|
3
3
|
// ─── Translations ───────────────────────────────────────────────────
|
|
4
|
-
export { getTranslations
|
|
4
|
+
export { getTranslations } from "./translations.js";
|
|
5
5
|
// ─── Interpolation utilities ────────────────────────────────────────
|
|
6
6
|
export { getNestedValue } from "./interpolation.js";
|
|
7
7
|
// ─── Sanitisation utilities ─────────────────────────────────────────
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type RichSegmentText = {
|
|
2
|
+
type: "text";
|
|
3
|
+
value: string;
|
|
4
|
+
};
|
|
5
|
+
export type RichSegmentTag = {
|
|
6
|
+
type: "tag";
|
|
7
|
+
tag: string;
|
|
8
|
+
chunks: string;
|
|
9
|
+
};
|
|
10
|
+
export type RichSegment = RichSegmentText | RichSegmentTag;
|
|
11
|
+
export declare function parseRichSegments(str: string, tagNames: string[]): RichSegment[];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { escapeRegExp } from "./sanitize.js";
|
|
2
|
+
// ─── Parse rich text into framework-agnostic segments ───────────────
|
|
3
|
+
export function parseRichSegments(str, tagNames) {
|
|
4
|
+
if (tagNames.length === 0) {
|
|
5
|
+
return str.length > 0 ? [{ type: "text", value: str }] : [];
|
|
6
|
+
}
|
|
7
|
+
const escaped = tagNames.map(escapeRegExp);
|
|
8
|
+
const regex = new RegExp(`<(${escaped.join("|")})>(.*?)<\\/(\\1)>`, "g");
|
|
9
|
+
const result = [];
|
|
10
|
+
let lastIndex = 0;
|
|
11
|
+
let match;
|
|
12
|
+
while ((match = regex.exec(str)) !== null) {
|
|
13
|
+
if (match.index > lastIndex) {
|
|
14
|
+
result.push({ type: "text", value: str.slice(lastIndex, match.index) });
|
|
15
|
+
}
|
|
16
|
+
const [, tag, chunks] = match;
|
|
17
|
+
result.push({ type: "tag", tag, chunks });
|
|
18
|
+
lastIndex = match.index + match[0].length;
|
|
19
|
+
}
|
|
20
|
+
if (lastIndex < str.length) {
|
|
21
|
+
result.push({ type: "text", value: str.slice(lastIndex) });
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AstroIntegration } from "astro";
|
|
2
2
|
import type { MessagesConfig, RoutesMap } from "./types/index.js";
|
|
3
|
-
import { setRequestLocale as _setRequestLocale, runWithLocale as _runWithLocale, getLocale as _getLocale, getLocales as _getLocales, isValidLocale as _isValidLocale, getMessages as _getMessages, getTranslations as _getTranslations,
|
|
3
|
+
import { setRequestLocale as _setRequestLocale, runWithLocale as _runWithLocale, getLocale as _getLocale, getLocales as _getLocales, isValidLocale as _isValidLocale, getMessages as _getMessages, getTranslations as _getTranslations, defineRequestConfig as _defineRequestConfig, __resetRequestConfig as _resetRequestConfig, path as _path, switchLocalePath as _switchLocalePath } from "./core.js";
|
|
4
4
|
export type AstroIntlOptions = {
|
|
5
5
|
enabled?: boolean;
|
|
6
6
|
defaultLocale?: string;
|
|
@@ -16,7 +16,6 @@ export declare const getLocales: typeof _getLocales;
|
|
|
16
16
|
export declare const isValidLocale: typeof _isValidLocale;
|
|
17
17
|
export declare const getMessages: typeof _getMessages;
|
|
18
18
|
export declare const getTranslations: typeof _getTranslations;
|
|
19
|
-
export declare const getTranslationsReact: typeof _getTranslationsReact;
|
|
20
19
|
export declare const defineRequestConfig: typeof _defineRequestConfig;
|
|
21
20
|
export declare const __resetRequestConfig: typeof _resetRequestConfig;
|
|
22
21
|
export declare const path: typeof _path;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { setRequestLocale as _setRequestLocale, runWithLocale as _runWithLocale, getLocale as _getLocale, getLocales as _getLocales, isValidLocale as _isValidLocale, getMessages as _getMessages, getTranslations as _getTranslations,
|
|
1
|
+
import { setRequestLocale as _setRequestLocale, runWithLocale as _runWithLocale, getLocale as _getLocale, getLocales as _getLocales, isValidLocale as _isValidLocale, getMessages as _getMessages, getTranslations as _getTranslations, defineRequestConfig as _defineRequestConfig, __resetRequestConfig as _resetRequestConfig, __setConfigMessages, __setIntlConfig, path as _path, switchLocalePath as _switchLocalePath, } from "./core.js";
|
|
2
2
|
export default function astroIntl(options = {}) {
|
|
3
3
|
const { enabled = true, defaultLocale, locales, messages, routes } = options;
|
|
4
4
|
if (defaultLocale || locales || routes) {
|
|
@@ -38,7 +38,6 @@ export const getLocales = _getLocales;
|
|
|
38
38
|
export const isValidLocale = _isValidLocale;
|
|
39
39
|
export const getMessages = _getMessages;
|
|
40
40
|
export const getTranslations = _getTranslations;
|
|
41
|
-
export const getTranslationsReact = _getTranslationsReact;
|
|
42
41
|
export const defineRequestConfig = _defineRequestConfig;
|
|
43
42
|
export const __resetRequestConfig = _resetRequestConfig;
|
|
44
43
|
export const path = _path;
|
package/dist/translations.d.ts
CHANGED
|
@@ -6,6 +6,3 @@ export declare function getTranslations<T extends Record<string, unknown> = Reco
|
|
|
6
6
|
tags: Record<string, (chunks: string) => string>;
|
|
7
7
|
}) => string;
|
|
8
8
|
};
|
|
9
|
-
export declare function getTranslationsReact<T extends Record<string, unknown> = Record<string, unknown>>(namespace?: string): ((key: DotPaths<T>) => string) & {
|
|
10
|
-
rich: (key: DotPaths<T>, tags: Record<string, (chunks: string) => import("react").ReactNode>) => import("react").ReactNode[];
|
|
11
|
-
};
|
package/dist/translations.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { getMessages
|
|
1
|
+
import { getMessages } from "./store.js";
|
|
2
2
|
import { getNestedValue, interpolateValues } from "./interpolation.js";
|
|
3
3
|
import { escapeRegExp, sanitizeHtml } from "./sanitize.js";
|
|
4
|
-
import { createGetTranslationsReact } from "./react.js";
|
|
5
4
|
// ─── getTranslations (Astro / plain HTML) ───────────────────────────
|
|
6
5
|
export function getTranslations(namespace) {
|
|
7
6
|
const messages = getMessages(namespace);
|
|
@@ -32,10 +31,3 @@ export function getTranslations(namespace) {
|
|
|
32
31
|
Object.assign(t, { markup });
|
|
33
32
|
return t;
|
|
34
33
|
}
|
|
35
|
-
// ─── getTranslationsReact (React islands) ───────────────────────────
|
|
36
|
-
export function getTranslationsReact(namespace) {
|
|
37
|
-
const messages = getMessages(namespace);
|
|
38
|
-
const locale = getLocale();
|
|
39
|
-
const ui = { [locale]: { default: messages } };
|
|
40
|
-
return createGetTranslationsReact(ui, locale)(locale, "default");
|
|
41
|
-
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-intl",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Sistema de internacionalización simple y type-safe para Astro
|
|
5
|
+
"description": "Sistema de internacionalización simple y type-safe para Astro.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"astro",
|
|
8
8
|
"astro-integration",
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
"internationalization",
|
|
13
13
|
"intl",
|
|
14
14
|
"translations",
|
|
15
|
-
"typescript"
|
|
16
|
-
"accessibility"
|
|
15
|
+
"typescript"
|
|
17
16
|
],
|
|
18
17
|
"author": "Erick Cruz <erickj.cruzs@gmail.com>",
|
|
19
18
|
"license": "MIT",
|
|
@@ -43,6 +42,14 @@
|
|
|
43
42
|
"types": "./dist/middleware.d.ts",
|
|
44
43
|
"import": "./dist/middleware.js"
|
|
45
44
|
},
|
|
45
|
+
"./react": {
|
|
46
|
+
"types": "./dist/adapters/react.d.ts",
|
|
47
|
+
"import": "./dist/adapters/react.js"
|
|
48
|
+
},
|
|
49
|
+
"./svelte": {
|
|
50
|
+
"types": "./dist/adapters/svelte.d.ts",
|
|
51
|
+
"import": "./dist/adapters/svelte.js"
|
|
52
|
+
},
|
|
46
53
|
"./routing": {
|
|
47
54
|
"types": "./dist/routing.d.ts",
|
|
48
55
|
"import": "./dist/routing.js"
|
|
@@ -54,6 +61,9 @@
|
|
|
54
61
|
"peerDependenciesMeta": {
|
|
55
62
|
"react": {
|
|
56
63
|
"optional": true
|
|
64
|
+
},
|
|
65
|
+
"svelte": {
|
|
66
|
+
"optional": true
|
|
57
67
|
}
|
|
58
68
|
},
|
|
59
69
|
"repository": {
|
|
@@ -81,6 +91,7 @@
|
|
|
81
91
|
"react-dom": "^19.2.4",
|
|
82
92
|
"typescript": "^5.9.3",
|
|
83
93
|
"typescript-eslint": "^8.19.1",
|
|
94
|
+
"svelte": "^5.0.0",
|
|
84
95
|
"vitest": "^4.0.18"
|
|
85
96
|
}
|
|
86
97
|
}
|