@i18n-micro/astro 1.3.25 → 2.0.1
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/LICENSE +1 -1
- package/README.md +8 -64
- package/dist/auto-middleware.d.ts +1 -0
- package/dist/client/core.d.cts +86 -0
- package/dist/client/core.d.ts +71 -8
- package/dist/client/index.d.cts +2 -6
- package/dist/client/index.d.ts +2 -6
- package/dist/client/index.js +1 -6
- package/dist/client/preact.d.ts +12 -23
- package/dist/client/preact.js +13 -37
- package/dist/client/react.d.ts +14 -21
- package/dist/client/react.js +12 -37
- package/dist/client/svelte.d.ts +14 -14
- package/dist/client/svelte.js +17 -47
- package/dist/client/vue.d.ts +15 -15
- package/dist/client/vue.js +37 -2
- package/dist/composer.d.cts +16 -0
- package/dist/composer.d.ts +10 -30
- package/dist/core-CgrgdmGy.cjs +1 -0
- package/dist/core-DTAcn2yF.js +72 -0
- package/dist/env.d.ts +8 -2
- package/dist/index.cjs +11 -3
- package/dist/index.d.cts +10 -13
- package/dist/index.d.ts +10 -13
- package/dist/index.mjs +171 -482
- package/dist/integration.d.cts +26 -0
- package/dist/integration.d.ts +14 -15
- package/dist/middleware-BT3OA5YL.cjs +1 -0
- package/dist/middleware-Ch8E0iV_.js +257 -0
- package/dist/middleware.d.cts +34 -0
- package/dist/middleware.d.ts +17 -8
- package/dist/middleware.js +23 -0
- package/dist/node.d.ts +26 -0
- package/dist/node.js +22 -0
- package/dist/request.d.ts +69 -0
- package/dist/router/adapter.d.cts +9 -0
- package/dist/router/adapter.d.ts +5 -4
- package/dist/router/types.d.cts +66 -0
- package/dist/router/types.d.ts +3 -2
- package/dist/utils.d.cts +112 -0
- package/dist/utils.d.ts +39 -35
- package/dist/virtual.d.ts +21 -0
- package/package.json +28 -15
- package/src/auto-middleware.ts +26 -0
- package/src/client/core.ts +124 -59
- package/src/client/index.ts +2 -11
- package/src/client/preact.tsx +12 -127
- package/src/client/react.tsx +11 -123
- package/src/client/svelte.ts +6 -69
- package/src/client/vue.ts +22 -77
- package/src/components/i18n-link.astro +2 -59
- package/src/components/i18n-switcher.astro +46 -149
- package/src/components/i18n-t.astro +6 -54
- package/src/composer.ts +25 -115
- package/src/env.d.ts +7 -0
- package/src/index.ts +1 -6
- package/src/integration.ts +113 -52
- package/src/middleware.ts +174 -120
- package/src/node.ts +51 -0
- package/src/request.ts +177 -0
- package/src/router/adapter.ts +47 -149
- package/src/router/types.ts +3 -2
- package/src/utils.ts +149 -323
- package/src/virtual.d.ts +21 -0
- package/dist/components/index.d.cts +0 -0
- package/dist/components/index.d.ts +0 -0
- package/dist/core-DwkpOV-H.cjs +0 -1
- package/dist/core-VSnkGRWR.js +0 -28
- package/dist/load-translations.d.ts +0 -43
- package/dist/routing-context.d.ts +0 -4
- package/dist/vue-BNiEwZr2.cjs +0 -5
- package/dist/vue-CWHjIOiS.js +0 -1724
- package/src/components/index.ts +0 -11
- package/src/load-translations.ts +0 -106
- package/src/routing-context.ts +0 -23
package/dist/router/types.d.ts
CHANGED
|
@@ -20,9 +20,10 @@ export interface I18nRoutingStrategy {
|
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
22
|
* Get route name from path (e.g., /en/about -> about)
|
|
23
|
-
* Used in middleware to set route name
|
|
23
|
+
* Used in middleware to set route name. `routePattern` is the page's pattern (`/blog/[slug]`), with
|
|
24
|
+
* a `[locale]` param already replaced by its value: use it to give every URL of a page one name.
|
|
24
25
|
*/
|
|
25
|
-
getRouteName?: (path: string, locales: string[]) => string;
|
|
26
|
+
getRouteName?: (path: string, locales: string[], routePattern?: string) => string;
|
|
26
27
|
/**
|
|
27
28
|
* Get locale from path
|
|
28
29
|
* Checks if first segment is a locale code
|
package/dist/utils.d.cts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ResolveTextOptions } from '@i18n-micro/core';
|
|
2
|
+
import { CleanTranslation, Locale, Params, TranslationKey, Translations } from '@i18n-micro/types';
|
|
3
|
+
import { LocaleHead } from '@i18n-micro/utils/locale-head';
|
|
4
|
+
import { AstroGlobal } from 'astro';
|
|
5
|
+
import { AstroI18n } from './composer.cjs';
|
|
6
|
+
/** The subset of `AstroGlobal` the helpers read. */
|
|
7
|
+
type Astro = Pick<AstroGlobal, 'locals' | 'url'> & Partial<Pick<AstroGlobal, 'params' | 'site'>>;
|
|
8
|
+
/**
|
|
9
|
+
* Get i18n instance from Astro context
|
|
10
|
+
*/
|
|
11
|
+
export declare function getI18n(astro: Pick<AstroGlobal, 'locals'>): AstroI18n;
|
|
12
|
+
/**
|
|
13
|
+
* Get current locale from Astro context
|
|
14
|
+
*/
|
|
15
|
+
export declare function getLocale(astro: Pick<AstroGlobal, 'locals'>): string;
|
|
16
|
+
/**
|
|
17
|
+
* Get default locale from Astro context
|
|
18
|
+
*/
|
|
19
|
+
export declare function getDefaultLocale(astro: Pick<AstroGlobal, 'locals'>): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get all locales from Astro context
|
|
22
|
+
*/
|
|
23
|
+
export declare function getLocales(astro: Pick<AstroGlobal, 'locals'>): Locale[];
|
|
24
|
+
/**
|
|
25
|
+
* Use i18n in Astro pages/components
|
|
26
|
+
* Returns helper functions for translations
|
|
27
|
+
*/
|
|
28
|
+
export declare function useI18n(astro: Astro): {
|
|
29
|
+
locale: string;
|
|
30
|
+
defaultLocale: string;
|
|
31
|
+
locales: Locale[];
|
|
32
|
+
t: (key: TranslationKey, params?: Params, defaultValue?: string | null, routeContext?: unknown) => CleanTranslation;
|
|
33
|
+
ts: (key: TranslationKey, params?: Params, defaultValue?: string, routeContext?: unknown) => string;
|
|
34
|
+
tc: (key: TranslationKey, count: number | Params, defaultValue?: string) => string;
|
|
35
|
+
tn: {
|
|
36
|
+
(value: number, options?: Intl.NumberFormatOptions): string;
|
|
37
|
+
(value: number, key: string, overrides?: Intl.NumberFormatOptions): string;
|
|
38
|
+
(value: number, key: string, locale: string, overrides?: Intl.NumberFormatOptions): string;
|
|
39
|
+
};
|
|
40
|
+
td: {
|
|
41
|
+
(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
|
|
42
|
+
(value: Date | number | string, key: string, overrides?: Intl.DateTimeFormatOptions): string;
|
|
43
|
+
(value: Date | number | string, key: string, locale: string, overrides?: Intl.DateTimeFormatOptions): string;
|
|
44
|
+
};
|
|
45
|
+
tdr: (value: Date | number | string, options?: Intl.RelativeTimeFormatOptions) => string;
|
|
46
|
+
has: (key: TranslationKey, routeContext?: unknown) => boolean;
|
|
47
|
+
getRoute: () => string;
|
|
48
|
+
getRouteName: (path?: string) => string;
|
|
49
|
+
getLocaleFromPath: (path?: string) => string;
|
|
50
|
+
switchLocalePath: (newLocale: string) => string;
|
|
51
|
+
localizePath: (path: string, targetLocale?: string) => string;
|
|
52
|
+
getI18n: () => AstroI18n;
|
|
53
|
+
getBasePath: (url?: URL) => string;
|
|
54
|
+
addTranslations: (locale: string, translations: Translations, merge?: boolean) => void;
|
|
55
|
+
addRouteTranslations: (locale: string, routeName: string, translations: Translations, merge?: boolean) => void;
|
|
56
|
+
mergeTranslations: (locale: string, routeName: string, translations: Translations) => void;
|
|
57
|
+
clearCache: () => void;
|
|
58
|
+
};
|
|
59
|
+
/** What `<I18nSwitcher>` shows for a locale. A code is free-form: `constructor` must not read an inherited member. */
|
|
60
|
+
export declare function localeLabel(locale: Locale, customLabels: Record<string, string>): string;
|
|
61
|
+
/** `href` of an `<I18nLink>`: relative to the page, in `locale` (default the current one). */
|
|
62
|
+
export declare function resolveLinkHref(astro: Astro, href: string, locale?: string): string;
|
|
63
|
+
export interface I18nTProps extends ResolveTextOptions {
|
|
64
|
+
defaultValue?: string;
|
|
65
|
+
}
|
|
66
|
+
/** The text of an `<I18nT>`, `null` when `hideIfEmpty` hides it. With `html`, params arrive escaped; the translation is trusted. */
|
|
67
|
+
export declare function resolveI18nT(astro: Astro, props: I18nTProps): string | null;
|
|
68
|
+
/**
|
|
69
|
+
* Strip locale prefix from the current URL for `Astro.rewrite()` on `/[locale]/*` routes.
|
|
70
|
+
*/
|
|
71
|
+
export declare function getLocaleRewritePath(astro: Astro): string;
|
|
72
|
+
/**
|
|
73
|
+
* Validate `:locale`, sync i18n/locals, and return the base path for `Astro.rewrite()`.
|
|
74
|
+
* Returns a `Response` when the locale param is invalid.
|
|
75
|
+
*/
|
|
76
|
+
export declare function prepareLocaleRewrite(astro: Astro & Pick<AstroGlobal, 'params'>): Response | string;
|
|
77
|
+
/**
|
|
78
|
+
* Generate locale head meta tags for SEO
|
|
79
|
+
*/
|
|
80
|
+
export interface LocaleHeadOptions {
|
|
81
|
+
/** Origin of the absolute URLs. Default: `site` from the Astro config, else root-relative URLs. */
|
|
82
|
+
baseUrl?: string;
|
|
83
|
+
addDirAttribute?: boolean;
|
|
84
|
+
addSeoAttributes?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Also emit bare-language `hreflang` tags derived from `iso` (never from routing `code`).
|
|
87
|
+
* @default false
|
|
88
|
+
*/
|
|
89
|
+
hreflangBaseLanguage?: boolean;
|
|
90
|
+
}
|
|
91
|
+
export type LocaleHeadResult = LocaleHead;
|
|
92
|
+
export declare function useLocaleHead(astro: Astro, options?: LocaleHeadOptions): LocaleHeadResult;
|
|
93
|
+
/**
|
|
94
|
+
* Props to pass to client islands (Vue, React, Svelte, Preact)
|
|
95
|
+
*/
|
|
96
|
+
export interface I18nClientProps {
|
|
97
|
+
locale: string;
|
|
98
|
+
/** The tag plurals go by: the locale's `iso` when it has one. */
|
|
99
|
+
pluralLocale?: string;
|
|
100
|
+
/** Tags of the codes that have one, for `setLocale`. Absent when there are none. */
|
|
101
|
+
localeIso?: Record<string, string>;
|
|
102
|
+
fallbackLocale: string;
|
|
103
|
+
translations: Record<string, Translations>;
|
|
104
|
+
currentRoute: string;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Props for a client island: what `t()` can answer on this page (route, root and fallback keys),
|
|
108
|
+
* or only `keys` of that, which is the way to keep the page small. Stored trees are shared, an
|
|
109
|
+
* island must not write into them.
|
|
110
|
+
*/
|
|
111
|
+
export declare function getI18nProps(astro: Pick<AstroGlobal, 'locals'>, keys?: string[]): I18nClientProps;
|
|
112
|
+
export {};
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,32 +1,36 @@
|
|
|
1
|
+
import { ResolveTextOptions } from '@i18n-micro/core';
|
|
1
2
|
import { CleanTranslation, Locale, Params, TranslationKey, Translations } from '@i18n-micro/types';
|
|
3
|
+
import { LocaleHead } from '@i18n-micro/utils/locale-head';
|
|
2
4
|
import { AstroGlobal } from 'astro';
|
|
3
|
-
import { AstroI18n } from './composer';
|
|
5
|
+
import { AstroI18n } from './composer.js';
|
|
6
|
+
/** The subset of `AstroGlobal` the helpers read. */
|
|
7
|
+
type Astro = Pick<AstroGlobal, 'locals' | 'url'> & Partial<Pick<AstroGlobal, 'params' | 'site'>>;
|
|
4
8
|
/**
|
|
5
9
|
* Get i18n instance from Astro context
|
|
6
10
|
*/
|
|
7
|
-
export declare function getI18n(astro: AstroGlobal): AstroI18n;
|
|
11
|
+
export declare function getI18n(astro: Pick<AstroGlobal, 'locals'>): AstroI18n;
|
|
8
12
|
/**
|
|
9
13
|
* Get current locale from Astro context
|
|
10
14
|
*/
|
|
11
|
-
export declare function getLocale(astro: AstroGlobal): string;
|
|
15
|
+
export declare function getLocale(astro: Pick<AstroGlobal, 'locals'>): string;
|
|
12
16
|
/**
|
|
13
17
|
* Get default locale from Astro context
|
|
14
18
|
*/
|
|
15
|
-
export declare function getDefaultLocale(astro: AstroGlobal): string;
|
|
19
|
+
export declare function getDefaultLocale(astro: Pick<AstroGlobal, 'locals'>): string;
|
|
16
20
|
/**
|
|
17
21
|
* Get all locales from Astro context
|
|
18
22
|
*/
|
|
19
|
-
export declare function getLocales(astro: AstroGlobal): Locale[];
|
|
23
|
+
export declare function getLocales(astro: Pick<AstroGlobal, 'locals'>): Locale[];
|
|
20
24
|
/**
|
|
21
25
|
* Use i18n in Astro pages/components
|
|
22
26
|
* Returns helper functions for translations
|
|
23
27
|
*/
|
|
24
|
-
export declare function useI18n(astro:
|
|
28
|
+
export declare function useI18n(astro: Astro): {
|
|
25
29
|
locale: string;
|
|
26
30
|
defaultLocale: string;
|
|
27
31
|
locales: Locale[];
|
|
28
|
-
t: (key: TranslationKey, params?: Params, defaultValue?: string | null,
|
|
29
|
-
ts: (key: TranslationKey, params?: Params, defaultValue?: string,
|
|
32
|
+
t: (key: TranslationKey, params?: Params, defaultValue?: string | null, routeContext?: unknown) => CleanTranslation;
|
|
33
|
+
ts: (key: TranslationKey, params?: Params, defaultValue?: string, routeContext?: unknown) => string;
|
|
30
34
|
tc: (key: TranslationKey, count: number | Params, defaultValue?: string) => string;
|
|
31
35
|
tn: {
|
|
32
36
|
(value: number, options?: Intl.NumberFormatOptions): string;
|
|
@@ -39,7 +43,7 @@ export declare function useI18n(astro: AstroGlobal): {
|
|
|
39
43
|
(value: Date | number | string, key: string, locale: string, overrides?: Intl.DateTimeFormatOptions): string;
|
|
40
44
|
};
|
|
41
45
|
tdr: (value: Date | number | string, options?: Intl.RelativeTimeFormatOptions) => string;
|
|
42
|
-
has: (key: TranslationKey,
|
|
46
|
+
has: (key: TranslationKey, routeContext?: unknown) => boolean;
|
|
43
47
|
getRoute: () => string;
|
|
44
48
|
getRouteName: (path?: string) => string;
|
|
45
49
|
getLocaleFromPath: (path?: string) => string;
|
|
@@ -47,24 +51,34 @@ export declare function useI18n(astro: AstroGlobal): {
|
|
|
47
51
|
localizePath: (path: string, targetLocale?: string) => string;
|
|
48
52
|
getI18n: () => AstroI18n;
|
|
49
53
|
getBasePath: (url?: URL) => string;
|
|
50
|
-
addTranslations: (locale: string, translations:
|
|
51
|
-
addRouteTranslations: (locale: string, routeName: string, translations:
|
|
52
|
-
mergeTranslations: (locale: string, routeName: string, translations:
|
|
54
|
+
addTranslations: (locale: string, translations: Translations, merge?: boolean) => void;
|
|
55
|
+
addRouteTranslations: (locale: string, routeName: string, translations: Translations, merge?: boolean) => void;
|
|
56
|
+
mergeTranslations: (locale: string, routeName: string, translations: Translations) => void;
|
|
53
57
|
clearCache: () => void;
|
|
54
58
|
};
|
|
59
|
+
/** What `<I18nSwitcher>` shows for a locale. A code is free-form: `constructor` must not read an inherited member. */
|
|
60
|
+
export declare function localeLabel(locale: Locale, customLabels: Record<string, string>): string;
|
|
61
|
+
/** `href` of an `<I18nLink>`: relative to the page, in `locale` (default the current one). */
|
|
62
|
+
export declare function resolveLinkHref(astro: Astro, href: string, locale?: string): string;
|
|
63
|
+
export interface I18nTProps extends ResolveTextOptions {
|
|
64
|
+
defaultValue?: string;
|
|
65
|
+
}
|
|
66
|
+
/** The text of an `<I18nT>`, `null` when `hideIfEmpty` hides it. With `html`, params arrive escaped; the translation is trusted. */
|
|
67
|
+
export declare function resolveI18nT(astro: Astro, props: I18nTProps): string | null;
|
|
55
68
|
/**
|
|
56
69
|
* Strip locale prefix from the current URL for `Astro.rewrite()` on `/[locale]/*` routes.
|
|
57
70
|
*/
|
|
58
|
-
export declare function getLocaleRewritePath(astro:
|
|
71
|
+
export declare function getLocaleRewritePath(astro: Astro): string;
|
|
59
72
|
/**
|
|
60
73
|
* Validate `:locale`, sync i18n/locals, and return the base path for `Astro.rewrite()`.
|
|
61
74
|
* Returns a `Response` when the locale param is invalid.
|
|
62
75
|
*/
|
|
63
|
-
export declare function prepareLocaleRewrite(astro: AstroGlobal): Response | string;
|
|
76
|
+
export declare function prepareLocaleRewrite(astro: Astro & Pick<AstroGlobal, 'params'>): Response | string;
|
|
64
77
|
/**
|
|
65
78
|
* Generate locale head meta tags for SEO
|
|
66
79
|
*/
|
|
67
80
|
export interface LocaleHeadOptions {
|
|
81
|
+
/** Origin of the absolute URLs. Default: `site` from the Astro config, else root-relative URLs. */
|
|
68
82
|
baseUrl?: string;
|
|
69
83
|
addDirAttribute?: boolean;
|
|
70
84
|
addSeoAttributes?: boolean;
|
|
@@ -74,35 +88,25 @@ export interface LocaleHeadOptions {
|
|
|
74
88
|
*/
|
|
75
89
|
hreflangBaseLanguage?: boolean;
|
|
76
90
|
}
|
|
77
|
-
export
|
|
78
|
-
|
|
79
|
-
lang?: string;
|
|
80
|
-
dir?: 'ltr' | 'rtl' | 'auto';
|
|
81
|
-
};
|
|
82
|
-
link: Array<{
|
|
83
|
-
rel: string;
|
|
84
|
-
href: string;
|
|
85
|
-
hreflang?: string;
|
|
86
|
-
}>;
|
|
87
|
-
meta: Array<{
|
|
88
|
-
property: string;
|
|
89
|
-
content: string;
|
|
90
|
-
}>;
|
|
91
|
-
}
|
|
92
|
-
export declare function useLocaleHead(astro: AstroGlobal, options?: LocaleHeadOptions): LocaleHeadResult;
|
|
91
|
+
export type LocaleHeadResult = LocaleHead;
|
|
92
|
+
export declare function useLocaleHead(astro: Astro, options?: LocaleHeadOptions): LocaleHeadResult;
|
|
93
93
|
/**
|
|
94
94
|
* Props to pass to client islands (Vue, React, Svelte, Preact)
|
|
95
95
|
*/
|
|
96
96
|
export interface I18nClientProps {
|
|
97
97
|
locale: string;
|
|
98
|
+
/** The tag plurals go by: the locale's `iso` when it has one. */
|
|
99
|
+
pluralLocale?: string;
|
|
100
|
+
/** Tags of the codes that have one, for `setLocale`. Absent when there are none. */
|
|
101
|
+
localeIso?: Record<string, string>;
|
|
98
102
|
fallbackLocale: string;
|
|
99
103
|
translations: Record<string, Translations>;
|
|
100
104
|
currentRoute: string;
|
|
101
105
|
}
|
|
102
106
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* currentRoute is already normalized via middleware (getRouteName), so we use it directly.
|
|
107
|
+
* Props for a client island: what `t()` can answer on this page (route, root and fallback keys),
|
|
108
|
+
* or only `keys` of that, which is the way to keep the page small. Stored trees are shared, an
|
|
109
|
+
* island must not write into them.
|
|
107
110
|
*/
|
|
108
|
-
export declare function getI18nProps(astro: AstroGlobal, keys?: string[]): I18nClientProps;
|
|
111
|
+
export declare function getI18nProps(astro: Pick<AstroGlobal, 'locals'>, keys?: string[]): I18nClientProps;
|
|
112
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare module 'virtual:i18n-micro/config' {
|
|
2
|
+
export const config: {
|
|
3
|
+
defaultLocale: string
|
|
4
|
+
fallbackLocale: string
|
|
5
|
+
locales: import('@i18n-micro/types').Locale[]
|
|
6
|
+
localeCodes: string[]
|
|
7
|
+
translationDir: string | null
|
|
8
|
+
autoDetect: boolean
|
|
9
|
+
redirectToDefault: boolean
|
|
10
|
+
localeCookie: string | null
|
|
11
|
+
missingWarn: boolean
|
|
12
|
+
base: string
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare module 'virtual:i18n-micro/messages' {
|
|
17
|
+
export const messages: {
|
|
18
|
+
root: Record<string, import('@i18n-micro/types').Translations>
|
|
19
|
+
routes: Record<string, Record<string, import('@i18n-micro/types').Translations>>
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i18n-micro/astro",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Astro integration for i18n-micro — translations, routing, and islands.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
"url": "https://github.com/s00d/nuxt-i18n-micro/issues"
|
|
14
14
|
},
|
|
15
15
|
"license": "MIT",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "s00d",
|
|
18
|
+
"email": "Virus191288@gmail.com",
|
|
19
|
+
"url": "https://s00d.github.io/"
|
|
20
|
+
},
|
|
16
21
|
"repository": {
|
|
17
22
|
"type": "git",
|
|
18
23
|
"url": "git+https://github.com/s00d/nuxt-i18n-micro.git",
|
|
@@ -40,6 +45,14 @@
|
|
|
40
45
|
},
|
|
41
46
|
"default": "./dist/index.mjs"
|
|
42
47
|
},
|
|
48
|
+
"./middleware": {
|
|
49
|
+
"types": "./dist/auto-middleware.d.ts",
|
|
50
|
+
"import": "./dist/middleware.js"
|
|
51
|
+
},
|
|
52
|
+
"./node": {
|
|
53
|
+
"types": "./dist/node.d.ts",
|
|
54
|
+
"import": "./dist/node.js"
|
|
55
|
+
},
|
|
43
56
|
"./env": {
|
|
44
57
|
"types": "./dist/env.d.ts"
|
|
45
58
|
},
|
|
@@ -80,28 +93,28 @@
|
|
|
80
93
|
"access": "public"
|
|
81
94
|
},
|
|
82
95
|
"dependencies": {
|
|
83
|
-
"@i18n-micro/core": "^
|
|
84
|
-
"@i18n-micro/
|
|
85
|
-
"@i18n-micro/
|
|
86
|
-
"@i18n-micro/utils": "^
|
|
96
|
+
"@i18n-micro/core": "^2.0.1",
|
|
97
|
+
"@i18n-micro/types": "^2.0.1",
|
|
98
|
+
"@i18n-micro/runtime": "^2.0.1",
|
|
99
|
+
"@i18n-micro/utils": "^2.0.1"
|
|
87
100
|
},
|
|
88
101
|
"devDependencies": {
|
|
89
102
|
"astro": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
90
|
-
"preact": "^10.
|
|
91
|
-
"publint": "^0.3.
|
|
92
|
-
"react": "^
|
|
103
|
+
"preact": "^10.0.0 || ^11.0.0",
|
|
104
|
+
"publint": "^0.3.25",
|
|
105
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
93
106
|
"svelte": "^4.0.0 || ^5.0.0",
|
|
94
|
-
"vite": "^8.3.
|
|
95
|
-
"vite-plugin-dts": "^5.1.
|
|
96
|
-
"vitest": "^5.0
|
|
97
|
-
"vue": "^3.5.
|
|
107
|
+
"vite": "^8.3.5",
|
|
108
|
+
"vite-plugin-dts": "^5.1.2",
|
|
109
|
+
"vitest": "^5.1.0",
|
|
110
|
+
"vue": "^3.5.44"
|
|
98
111
|
},
|
|
99
112
|
"peerDependencies": {
|
|
100
113
|
"astro": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
101
|
-
"preact": "^10.
|
|
114
|
+
"preact": "^10.0.0 || ^11.0.0",
|
|
102
115
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
103
116
|
"svelte": "^4.0.0 || ^5.0.0",
|
|
104
|
-
"vue": "^3.
|
|
117
|
+
"vue": "^3.3.0"
|
|
105
118
|
},
|
|
106
119
|
"peerDependenciesMeta": {
|
|
107
120
|
"svelte": {
|
|
@@ -118,7 +131,7 @@
|
|
|
118
131
|
}
|
|
119
132
|
},
|
|
120
133
|
"engines": {
|
|
121
|
-
"node": ">=
|
|
134
|
+
"node": ">=20"
|
|
122
135
|
},
|
|
123
136
|
"scripts": {
|
|
124
137
|
"build": "vite build",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** `@i18n-micro/astro/middleware`: what `i18nIntegration` installs. Nothing here reads the disk, so it runs on the edge too. */
|
|
2
|
+
import { config } from 'virtual:i18n-micro/config'
|
|
3
|
+
import { messages } from 'virtual:i18n-micro/messages'
|
|
4
|
+
import { localeIsoMap } from '@i18n-micro/core'
|
|
5
|
+
import { AstroI18n } from './composer'
|
|
6
|
+
import { createI18nMiddleware } from './middleware'
|
|
7
|
+
|
|
8
|
+
const i18n = new AstroI18n({
|
|
9
|
+
locale: config.defaultLocale,
|
|
10
|
+
fallbackLocale: config.fallbackLocale,
|
|
11
|
+
missingWarn: config.missingWarn,
|
|
12
|
+
localeIso: localeIsoMap(config.locales),
|
|
13
|
+
messages: messages.root,
|
|
14
|
+
routeMessages: messages.routes,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export const onRequest = createI18nMiddleware({
|
|
18
|
+
i18n,
|
|
19
|
+
defaultLocale: config.defaultLocale,
|
|
20
|
+
locales: config.localeCodes,
|
|
21
|
+
localeObjects: config.locales,
|
|
22
|
+
autoDetect: config.autoDetect,
|
|
23
|
+
redirectToDefault: config.redirectToDefault,
|
|
24
|
+
localeCookie: config.localeCookie,
|
|
25
|
+
base: config.base,
|
|
26
|
+
})
|
package/src/client/core.ts
CHANGED
|
@@ -1,52 +1,37 @@
|
|
|
1
|
-
import { interpolate } from '@i18n-micro/core'
|
|
2
|
-
import type { Params, Translations } from '@i18n-micro/types'
|
|
1
|
+
import { convertible, defaultPlural, FormatService, interpolate, ownValue, resolveTranslation, textOf } from '@i18n-micro/core'
|
|
2
|
+
import type { CleanTranslation, Getter, Params, TranslationKey, Translations } from '@i18n-micro/types'
|
|
3
|
+
import type { I18nClientProps } from '../utils'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* I18n state for client islands
|
|
6
7
|
*/
|
|
7
8
|
export interface I18nState {
|
|
8
9
|
locale: string
|
|
10
|
+
/** The tag plurals go by, when it is not `locale`. */
|
|
11
|
+
pluralLocale?: string
|
|
12
|
+
/** Tags of the codes that have one: `setLocale` finds the new locale's here. */
|
|
13
|
+
localeIso?: Record<string, string>
|
|
9
14
|
fallbackLocale: string
|
|
10
15
|
translations: Record<string, Translations> // routeName -> translations
|
|
11
16
|
currentRoute: string
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
// Returns the value as-is, including objects (for nested translations)
|
|
16
|
-
function findTranslation<T = unknown>(translations: Translations | null, key: string): T | null {
|
|
17
|
-
if (translations === null || typeof key !== 'string') {
|
|
18
|
-
return null
|
|
19
|
-
}
|
|
19
|
+
type Value = string | number | boolean | Translations
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
for (const part of parts) {
|
|
30
|
-
if (value && typeof value === 'object' && value !== null && part in value) {
|
|
31
|
-
value = (value as Translations)[part]
|
|
32
|
-
} else {
|
|
33
|
-
return null
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Return value as-is (can be string, number, boolean, object, or null)
|
|
39
|
-
// This matches CleanTranslation type which allows objects
|
|
40
|
-
return (value as T) ?? null
|
|
21
|
+
/**
|
|
22
|
+
* The value at `key` in the route's dictionary, or null. Own keys only (core's `resolveTranslation`):
|
|
23
|
+
* `toString` or `__proto__` is a missing key, not `Object.prototype`'s.
|
|
24
|
+
*/
|
|
25
|
+
function lookup(state: I18nState, key: string, routeName?: string): Value | null {
|
|
26
|
+
const route = routeName || state.currentRoute
|
|
27
|
+
const translations = Object.prototype.hasOwnProperty.call(state.translations, route) ? state.translations[route] : undefined
|
|
28
|
+
return (resolveTranslation(translations, key) as Value | null) ?? null
|
|
41
29
|
}
|
|
42
30
|
|
|
43
31
|
/**
|
|
44
|
-
* Pure function to get a translation from state
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* It supports basic translation lookup and interpolation.
|
|
48
|
-
* Returns CleanTranslation which can be string, number, boolean, object, or null.
|
|
49
|
-
* For advanced features like Linked Messages (@:path.to.key), use the server-side i18n instance.
|
|
32
|
+
* Pure function to get a translation from state, answering as the server's `t()` does over the
|
|
33
|
+
* same keys. `getI18nProps` already merged the root and fallback dictionaries into the state.
|
|
34
|
+
* For Linked Messages (@:path.to.key), use the server-side i18n instance.
|
|
50
35
|
*/
|
|
51
36
|
export function translate(
|
|
52
37
|
state: I18nState,
|
|
@@ -55,41 +40,121 @@ export function translate(
|
|
|
55
40
|
defaultValue?: string | null,
|
|
56
41
|
routeName?: string,
|
|
57
42
|
): string | number | boolean | Translations | null {
|
|
58
|
-
if (!key)
|
|
59
|
-
|
|
43
|
+
if (!key) return ''
|
|
44
|
+
// `??`: 0, false and '' are values, as on the server
|
|
45
|
+
const value = lookup(state, key, routeName) ?? (defaultValue || key)
|
|
46
|
+
if (typeof value === 'object') return convertible(value)
|
|
47
|
+
return typeof value === 'string' && params ? interpolate(value, params) : value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* `tc` of every island adapter, as the server's `BaseI18n.tc`: a missing key gives `defaultValue`
|
|
52
|
+
* (else the key), because the getter reports the miss to `defaultPlural` instead of the key.
|
|
53
|
+
*/
|
|
54
|
+
export function translatePlural(state: I18nState, key: TranslationKey, count: number | Params, defaultValue?: string): string {
|
|
55
|
+
const { count: countValue, ...params } = typeof count === 'number' ? { count } : count
|
|
56
|
+
if (countValue === undefined || countValue === null) return defaultValue ?? key
|
|
57
|
+
const getter: Getter = (k, p, dv) => {
|
|
58
|
+
const raw = lookup(state, k)
|
|
59
|
+
if (raw === null) return dv === undefined ? undefined : dv || k
|
|
60
|
+
return p && typeof raw === 'string' ? interpolate(raw, p) : raw
|
|
60
61
|
}
|
|
62
|
+
return defaultPlural(key, Number(countValue), params, state.pluralLocale ?? state.locale, getter) ?? defaultValue ?? key
|
|
63
|
+
}
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Checks if a translation exists in the state: a subtree counts, as on the server
|
|
67
|
+
*/
|
|
68
|
+
export function hasTranslation(state: I18nState, key: string, routeName?: string): boolean {
|
|
69
|
+
return lookup(state, key, routeName) !== null
|
|
70
|
+
}
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
/** `state` in another locale, with that locale's plural tag. */
|
|
73
|
+
export const withLocale = (state: I18nState, locale: string): I18nState => ({
|
|
74
|
+
...state,
|
|
75
|
+
locale,
|
|
76
|
+
pluralLocale: state.localeIso && (ownValue(state.localeIso, locale) as string | undefined),
|
|
77
|
+
})
|
|
68
78
|
|
|
69
|
-
|
|
70
|
-
if (!value) {
|
|
71
|
-
value = defaultValue === undefined ? key : defaultValue || key
|
|
72
|
-
}
|
|
79
|
+
const formatter = /*#__PURE__*/ new FormatService()
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
const isoTag = (state: I18nState): string => state.pluralLocale ?? state.locale
|
|
82
|
+
|
|
83
|
+
/** What every island adapter returns, over the state it reads at each call. */
|
|
84
|
+
export function createIslandApi(getState: () => I18nState) {
|
|
85
|
+
const t = (key: TranslationKey, params?: Params, defaultValue?: string | null, routeName?: string): CleanTranslation =>
|
|
86
|
+
translate(getState(), key as string, params, defaultValue, routeName)
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
t,
|
|
90
|
+
ts: (key: TranslationKey, params?: Params, defaultValue?: string, routeName?: string): string =>
|
|
91
|
+
textOf(t(key, params, defaultValue, routeName)) ?? defaultValue ?? (key as string),
|
|
92
|
+
tc: (key: TranslationKey, count: number | Params, defaultValue?: string): string => translatePlural(getState(), key, count, defaultValue),
|
|
93
|
+
tn: (value: number, options?: Intl.NumberFormatOptions): string => formatter.formatNumber(value, isoTag(getState()), options),
|
|
94
|
+
td: (value: Date | number | string, options?: Intl.DateTimeFormatOptions): string => formatter.formatDate(value, isoTag(getState()), options),
|
|
95
|
+
tdr: (value: Date | number | string, options?: Intl.RelativeTimeFormatOptions): string =>
|
|
96
|
+
formatter.formatRelativeTime(value, isoTag(getState()), options),
|
|
97
|
+
has: (key: TranslationKey, routeName?: string): boolean => hasTranslation(getState(), key as string, routeName),
|
|
98
|
+
getRoute: (): string => getState().currentRoute,
|
|
76
99
|
}
|
|
77
|
-
return value
|
|
78
100
|
}
|
|
79
101
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
type SetI18nState = (update: I18nState | ((current: I18nState) => I18nState)) => void
|
|
103
|
+
|
|
104
|
+
export interface I18nContextValue {
|
|
105
|
+
state: I18nState
|
|
106
|
+
setState: SetI18nState
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** What an island binding needs of its library: core stays free of framework imports. */
|
|
110
|
+
interface IslandLibrary<Children, Element, Context extends { Provider: unknown }> {
|
|
111
|
+
createContext: (value: I18nContextValue | null) => Context
|
|
112
|
+
createElement: (type: Context['Provider'], props: { value: I18nContextValue }, children: Children) => Element
|
|
113
|
+
useCallback: <T extends (arg: string) => void>(callback: T, deps: unknown[]) => T
|
|
114
|
+
useContext: (context: Context) => I18nContextValue | null
|
|
115
|
+
useMemo: <T>(factory: () => T, deps: unknown[]) => T
|
|
116
|
+
useState: <T>(initial: T) => [T, (update: T | ((current: T) => T)) => void]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** The provider and hook of an island library (Preact and React have one API). */
|
|
120
|
+
export function createIslandBinding<Children, Element, Context extends { Provider: unknown }>(lib: IslandLibrary<Children, Element, Context>) {
|
|
121
|
+
const { createContext, createElement, useCallback, useContext, useMemo, useState } = lib
|
|
122
|
+
const I18nContext = createContext(null)
|
|
123
|
+
|
|
124
|
+
/** A new `value` (an island with `transition:persist`) replaces the state. */
|
|
125
|
+
function I18nProvider({ children, value }: { children: Children; value: I18nClientProps }): Element {
|
|
126
|
+
const [state, setState] = useState<I18nState>(value)
|
|
127
|
+
const [seen, setSeen] = useState(value)
|
|
128
|
+
if (seen !== value) {
|
|
129
|
+
setSeen(value)
|
|
130
|
+
setState(value)
|
|
131
|
+
}
|
|
132
|
+
const context = useMemo(() => ({ state, setState }), [state])
|
|
133
|
+
return createElement(I18nContext.Provider, { value: context }, children)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function useAstroI18n() {
|
|
137
|
+
const context = useContext(I18nContext)
|
|
138
|
+
if (!context) {
|
|
139
|
+
throw new Error('useAstroI18n must be used within an I18nProvider')
|
|
140
|
+
}
|
|
141
|
+
const { state, setState } = context
|
|
142
|
+
const api = useMemo(() => createIslandApi(() => state), [state])
|
|
143
|
+
const setLocale = useCallback((locale: string) => setState((current) => withLocale(current, locale)), [setState])
|
|
144
|
+
const setRoute = useCallback((currentRoute: string) => setState((current) => ({ ...current, currentRoute })), [setState])
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
...api,
|
|
148
|
+
|
|
149
|
+
// Locale state
|
|
150
|
+
locale: state.locale,
|
|
151
|
+
fallbackLocale: state.fallbackLocale,
|
|
152
|
+
currentRoute: state.currentRoute,
|
|
86
153
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (value !== null && typeof value !== 'object') {
|
|
90
|
-
return true
|
|
154
|
+
setLocale,
|
|
155
|
+
setRoute,
|
|
91
156
|
}
|
|
92
157
|
}
|
|
93
158
|
|
|
94
|
-
return
|
|
159
|
+
return { I18nProvider, useAstroI18n }
|
|
95
160
|
}
|
package/src/client/index.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
1
|
+
// The pure core. Framework adapters live in their own entries (`./client/vue`, `./client/react`,
|
|
2
|
+
// `./client/preact`, `./client/svelte`): each needs its framework installed, and an island needs one.
|
|
3
3
|
export type { I18nState } from './core'
|
|
4
4
|
export { hasTranslation, translate } from './core'
|
|
5
|
-
// Preact adapter
|
|
6
|
-
export { I18nProvider as I18nProviderPreact, useAstroI18n as useAstroI18nPreact } from './preact'
|
|
7
|
-
|
|
8
|
-
// React adapter
|
|
9
|
-
export { I18nProvider, useAstroI18n as useAstroI18nReact } from './react'
|
|
10
|
-
// Svelte adapter
|
|
11
|
-
export { createI18nStore, useAstroI18n as useAstroI18nSvelte } from './svelte'
|
|
12
|
-
// Vue adapter
|
|
13
|
-
export { provideI18n, useAstroI18n as useAstroI18nVue } from './vue'
|