@fluixi/core 1.0.0-alpha.82 → 1.0.0-alpha.84
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/dist/cdn/core.global.js +2 -1
- package/dist/cli/generate.cjs +4117 -292
- package/dist/cli/generate.mjs +4122 -292
- package/dist/cli/index.cjs +4117 -292
- package/dist/cli/index.mjs +4122 -292
- package/dist/cli/run.cjs +63 -1
- package/dist/cli/run.mjs +39 -1
- package/dist/src/cdn/chunk-VNLCQBFQ.mjs +747 -0
- package/dist/src/cdn/core.cjs +1279 -1
- package/dist/src/cdn/core.d.ts +3 -3
- package/dist/src/cdn/core.js +3 -3
- package/dist/src/cdn/core.mjs +642 -1
- package/dist/src/cdn/router-WMN2AA7E.mjs +66 -0
- package/dist/src/index.cjs +1201 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -11
- package/dist/src/index.mjs +1313 -1
- package/dist/src/jsx-dev-runtime.cjs +26 -1
- package/dist/src/jsx-dev-runtime.mjs +24 -1
- package/dist/src/jsx-runtime.cjs +26 -1
- package/dist/src/jsx-runtime.js +0 -9
- package/dist/src/jsx-runtime.mjs +24 -1
- package/dist/src/lib/client/chunk-2C2KDDQV.mjs +133 -0
- package/dist/src/lib/client/index.cjs +870 -1
- package/dist/src/lib/client/index.d.ts +10 -14
- package/dist/src/lib/client/index.d.ts.map +1 -1
- package/dist/src/lib/client/index.js +36 -21
- package/dist/src/lib/client/index.mjs +222 -1
- package/dist/src/lib/client/router-X526HSWQ.mjs +548 -0
- package/dist/src/lib/context/context.cjs +118 -1
- package/dist/src/lib/context/context.d.ts +20 -38
- package/dist/src/lib/context/context.d.ts.map +1 -1
- package/dist/src/lib/context/context.js +10 -47
- package/dist/src/lib/context/context.mjs +99 -1
- package/dist/src/lib/context/index.cjs +120 -1
- package/dist/src/lib/context/index.mjs +99 -1
- package/dist/src/lib/control-flow.cjs +35 -1
- package/dist/src/lib/control-flow.mjs +25 -1
- package/dist/src/lib/core.cjs +1200 -1
- package/dist/src/lib/core.d.ts +0 -2
- package/dist/src/lib/core.d.ts.map +1 -1
- package/dist/src/lib/core.js +14 -48
- package/dist/src/lib/core.mjs +1313 -1
- package/dist/src/lib/env/index.cjs +60 -1
- package/dist/src/lib/env/index.d.ts +7 -8
- package/dist/src/lib/env/index.d.ts.map +1 -1
- package/dist/src/lib/env/index.js +14 -9
- package/dist/src/lib/env/index.mjs +39 -1
- package/dist/src/lib/i18n/index.cjs +108 -1
- package/dist/src/lib/i18n/index.d.ts +31 -10
- package/dist/src/lib/i18n/index.d.ts.map +1 -1
- package/dist/src/lib/i18n/index.js +31 -10
- package/dist/src/lib/i18n/index.mjs +88 -1
- package/dist/src/lib/index.cjs +633 -1
- package/dist/src/lib/index.d.ts +0 -2
- package/dist/src/lib/index.d.ts.map +1 -1
- package/dist/src/lib/index.js +5 -11
- package/dist/src/lib/index.mjs +721 -1
- package/dist/src/lib/isomorphic.cjs +85 -1
- package/dist/src/lib/isomorphic.d.ts +41 -8
- package/dist/src/lib/isomorphic.d.ts.map +1 -1
- package/dist/src/lib/isomorphic.js +38 -5
- package/dist/src/lib/isomorphic.mjs +64 -1
- package/dist/src/lib/jsx-runtime/index.cjs +21 -1
- package/dist/src/lib/jsx-runtime/index.d.ts +1 -1
- package/dist/src/lib/jsx-runtime/index.js +1 -11
- package/dist/src/lib/jsx-runtime/index.mjs +4 -1
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +21 -1
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +4 -1
- package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +302 -8
- package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts +15 -1
- package/dist/src/lib/plugins/fluixi-routes-plugin.d.ts.map +1 -1
- package/dist/src/lib/plugins/fluixi-routes-plugin.js +19 -5
- package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +271 -8
- package/dist/src/lib/plugins/index.cjs +311 -8
- package/dist/src/lib/plugins/index.mjs +279 -8
- package/dist/src/lib/plugins/route-codegen.cjs +73 -8
- package/dist/src/lib/plugins/route-codegen.d.ts +0 -1
- package/dist/src/lib/plugins/route-codegen.d.ts.map +1 -1
- package/dist/src/lib/plugins/route-codegen.js +4 -5
- package/dist/src/lib/plugins/route-codegen.mjs +52 -8
- package/dist/src/lib/plugins/route-scanner.cjs +189 -1
- package/dist/src/lib/plugins/route-scanner.d.ts +1 -2
- package/dist/src/lib/plugins/route-scanner.d.ts.map +1 -1
- package/dist/src/lib/plugins/route-scanner.js +7 -8
- package/dist/src/lib/plugins/route-scanner.mjs +158 -1
- package/dist/src/lib/plugins/vite.cjs +27 -1
- package/dist/src/lib/plugins/vite.mjs +6 -1
- package/dist/src/lib/render/await.cjs +38 -1
- package/dist/src/lib/render/await.d.ts +18 -0
- package/dist/src/lib/render/await.d.ts.map +1 -1
- package/dist/src/lib/render/await.js +20 -2
- package/dist/src/lib/render/await.mjs +17 -1
- package/dist/src/lib/render/component.cjs +221 -1
- package/dist/src/lib/render/component.d.ts +3 -44
- package/dist/src/lib/render/component.d.ts.map +1 -1
- package/dist/src/lib/render/component.js +32 -59
- package/dist/src/lib/render/component.mjs +214 -1
- package/dist/src/lib/render/deferred.cjs +175 -1
- package/dist/src/lib/render/deferred.d.ts.map +1 -1
- package/dist/src/lib/render/deferred.js +8 -8
- package/dist/src/lib/render/deferred.mjs +158 -1
- package/dist/src/lib/render/index.cjs +573 -1
- package/dist/src/lib/render/index.d.ts +3 -17
- package/dist/src/lib/render/index.d.ts.map +1 -1
- package/dist/src/lib/render/index.js +12 -26
- package/dist/src/lib/render/index.mjs +590 -1
- package/dist/src/lib/render/lazy.cjs +67 -1
- package/dist/src/lib/render/lazy.d.ts +22 -1
- package/dist/src/lib/render/lazy.d.ts.map +1 -1
- package/dist/src/lib/render/lazy.js +47 -8
- package/dist/src/lib/render/lazy.mjs +50 -1
- package/dist/src/lib/render/suspense.cjs +141 -1
- package/dist/src/lib/render/suspense.d.ts +40 -0
- package/dist/src/lib/render/suspense.d.ts.map +1 -1
- package/dist/src/lib/render/suspense.js +47 -7
- package/dist/src/lib/render/suspense.mjs +136 -1
- package/dist/src/lib/render/versions.cjs +35 -1
- package/dist/src/lib/render/versions.js +6 -6
- package/dist/src/lib/render/versions.mjs +14 -1
- package/dist/src/lib/router/data.cjs +230 -1
- package/dist/src/lib/router/data.d.ts +1 -13
- package/dist/src/lib/router/data.d.ts.map +1 -1
- package/dist/src/lib/router/data.js +1 -13
- package/dist/src/lib/router/data.mjs +214 -1
- package/dist/src/lib/router/index.cjs +627 -1
- package/dist/src/lib/router/index.d.ts +62 -10
- package/dist/src/lib/router/index.d.ts.map +1 -1
- package/dist/src/lib/router/index.js +90 -39
- package/dist/src/lib/router/index.mjs +639 -1
- package/dist/src/lib/server/hydration.cjs +169 -1
- package/dist/src/lib/server/hydration.d.ts +15 -55
- package/dist/src/lib/server/hydration.d.ts.map +1 -1
- package/dist/src/lib/server/hydration.js +23 -62
- package/dist/src/lib/server/hydration.mjs +148 -1
- package/dist/src/lib/server/index.cjs +401 -11
- package/dist/src/lib/server/index.d.ts +30 -6
- package/dist/src/lib/server/index.d.ts.map +1 -1
- package/dist/src/lib/server/index.js +34 -11
- package/dist/src/lib/server/index.mjs +379 -11
- package/dist/src/lib/server/reactive.cjs +197 -1
- package/dist/src/lib/server/reactive.d.ts +18 -77
- package/dist/src/lib/server/reactive.d.ts.map +1 -1
- package/dist/src/lib/server/reactive.js +19 -78
- package/dist/src/lib/server/reactive.mjs +176 -1
- package/dist/src/lib/theme/index.cjs +110 -1
- package/dist/src/lib/theme/index.d.ts +19 -4
- package/dist/src/lib/theme/index.d.ts.map +1 -1
- package/dist/src/lib/theme/index.js +17 -2
- package/dist/src/lib/theme/index.mjs +89 -1
- package/dist/src/lib/utils/index.cjs +209 -1
- package/dist/src/lib/utils/index.d.ts +0 -93
- package/dist/src/lib/utils/index.d.ts.map +1 -1
- package/dist/src/lib/utils/index.js +0 -93
- package/dist/src/lib/utils/index.mjs +188 -1
- package/dist/src/routes.cjs +37 -1
- package/dist/src/routes.d.ts +1 -1
- package/dist/src/routes.js +2 -2
- package/dist/src/routes.mjs +17 -1
- package/dist/src/server.cjs +400 -11
- package/dist/src/server.d.ts +0 -1
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +0 -1
- package/dist/src/server.mjs +377 -11
- package/dist/src/utils.cjs +21 -1
- package/dist/src/utils.d.ts +1 -1
- package/dist/src/utils.js +1 -1
- package/dist/src/utils.mjs +4 -1
- package/dist/src/version.generated.cjs +26 -1
- package/dist/src/version.generated.d.ts +1 -1
- package/dist/src/version.generated.js +2 -2
- package/dist/src/version.generated.mjs +5 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +17 -16
- package/src/index.ts +2 -14
- package/dist/src/cdn/chunk-RN7C7HNS.mjs +0 -1
- package/dist/src/cdn/router-QB3W43UQ.mjs +0 -1
- package/dist/src/lib/client/chunk-FT22BLCQ.mjs +0 -1
- package/dist/src/lib/client/router-IH7JYMRU.mjs +0 -1
|
@@ -1 +1,60 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/env/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
clientOnly: () => clientOnly,
|
|
24
|
+
createMemoryStorage: () => createMemoryStorage,
|
|
25
|
+
env: () => env,
|
|
26
|
+
isClient: () => isClient,
|
|
27
|
+
isServer: () => import_dom.isServer,
|
|
28
|
+
storage: () => storage
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
var import_dom = require("@fluixi/dom");
|
|
32
|
+
var import_dom2 = require("@fluixi/dom");
|
|
33
|
+
function isClient() {
|
|
34
|
+
return !(0, import_dom2.isServer)();
|
|
35
|
+
}
|
|
36
|
+
function createMemoryStorage() {
|
|
37
|
+
const mem = /* @__PURE__ */ new Map();
|
|
38
|
+
return {
|
|
39
|
+
getItem: (k) => mem.has(k) ? mem.get(k) : null,
|
|
40
|
+
setItem: (k, v) => void mem.set(k, String(v)),
|
|
41
|
+
removeItem: (k) => void mem.delete(k),
|
|
42
|
+
clear: () => mem.clear(),
|
|
43
|
+
key: (i) => Array.from(mem.keys())[i] ?? null,
|
|
44
|
+
get length() {
|
|
45
|
+
return mem.size;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
var storage = typeof localStorage !== "undefined" ? localStorage : createMemoryStorage();
|
|
50
|
+
var env = {
|
|
51
|
+
get window() {
|
|
52
|
+
return typeof window !== "undefined" ? window : void 0;
|
|
53
|
+
},
|
|
54
|
+
get document() {
|
|
55
|
+
return typeof document !== "undefined" ? document : void 0;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function clientOnly(fn, fallback) {
|
|
59
|
+
return isClient() ? fn() : fallback;
|
|
60
|
+
}
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Universal environment utilities
|
|
2
|
+
* Universal environment utilities, the same imports work on the server and the
|
|
3
3
|
* client, so app code never reaches for raw globals (`localStorage`, `window`,
|
|
4
4
|
* `document`) that don't exist under SSR and crash at module-eval time. This
|
|
5
|
-
* replaces hand-rolled `globalThis.localStorage =
|
|
5
|
+
* replaces hand-rolled `globalThis.localStorage = ...` shims and scattered
|
|
6
6
|
* `typeof window === 'undefined'` guards.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
export declare const isServer: boolean;
|
|
8
|
+
export { isServer } from '@fluixi/dom';
|
|
10
9
|
/** True in a browser (or jsdom) environment. */
|
|
11
|
-
export declare
|
|
10
|
+
export declare function isClient(): boolean;
|
|
12
11
|
/** A Map-backed in-memory implementation of the Web Storage API (server/tests). */
|
|
13
12
|
export declare function createMemoryStorage(): Storage;
|
|
14
13
|
/**
|
|
15
14
|
* Universal key/value storage with the exact `localStorage` API. On the client it
|
|
16
|
-
*
|
|
15
|
+
* is `localStorage`; on the server (no DOM) it is an ephemeral in-memory store, so
|
|
17
16
|
* a module that reads `storage.getItem(...)` at import time never crashes under SSR.
|
|
18
17
|
* Server reads/writes do not persist across requests (a later pass can back them with
|
|
19
18
|
* the request cookie for true per-user server state).
|
|
@@ -22,8 +21,8 @@ export declare const storage: Storage;
|
|
|
22
21
|
/**
|
|
23
22
|
* Lazy, typed accessors for the browser globals. They return the real object on the
|
|
24
23
|
* client and `undefined` on the server, so `env.window?.matchMedia(...)` is safe and
|
|
25
|
-
* typed everywhere. Deliberately
|
|
26
|
-
* would return wrong values and cause hydration mismatches
|
|
24
|
+
* typed everywhere. Deliberately not faked on the server: a stub `location`/`innerWidth`
|
|
25
|
+
* would return wrong values and cause hydration mismatches: guard with `?.` instead.
|
|
27
26
|
*/
|
|
28
27
|
export declare const env: {
|
|
29
28
|
readonly window: (Window & typeof globalThis) | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/env/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/env/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,gDAAgD;AAChD,wBAAgB,QAAQ,IAAI,OAAO,CAElC;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,IAAI,OAAO,CAY7C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,OACsD,CAAC;AAE7E;;;;;GAKG;AACH,eAAO,MAAM,GAAG;qBACA,CAAC,MAAM,GAAG,OAAO,UAAU,CAAC,GAAG,SAAS;uBAGtC,QAAQ,GAAG,SAAS;CAGrC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;AAC1D,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC"}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Universal environment utilities
|
|
2
|
+
* Universal environment utilities, the same imports work on the server and the
|
|
3
3
|
* client, so app code never reaches for raw globals (`localStorage`, `window`,
|
|
4
4
|
* `document`) that don't exist under SSR and crash at module-eval time. This
|
|
5
|
-
* replaces hand-rolled `globalThis.localStorage =
|
|
5
|
+
* replaces hand-rolled `globalThis.localStorage = ...` shims and scattered
|
|
6
6
|
* `typeof window === 'undefined'` guards.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// One answer for the whole framework, from the renderer that decides it. The constant
|
|
9
|
+
// this used to be was read once at module eval, so it could not see renderToString turn
|
|
10
|
+
// server mode on and reported false for the whole of a server render.
|
|
11
|
+
export { isServer } from '@fluixi/dom';
|
|
12
|
+
import { isServer as onServer } from '@fluixi/dom';
|
|
10
13
|
/** True in a browser (or jsdom) environment. */
|
|
11
|
-
export
|
|
14
|
+
export function isClient() {
|
|
15
|
+
return !onServer();
|
|
16
|
+
}
|
|
12
17
|
/** A Map-backed in-memory implementation of the Web Storage API (server/tests). */
|
|
13
18
|
export function createMemoryStorage() {
|
|
14
19
|
const mem = new Map();
|
|
@@ -25,7 +30,7 @@ export function createMemoryStorage() {
|
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
27
32
|
* Universal key/value storage with the exact `localStorage` API. On the client it
|
|
28
|
-
*
|
|
33
|
+
* is `localStorage`; on the server (no DOM) it is an ephemeral in-memory store, so
|
|
29
34
|
* a module that reads `storage.getItem(...)` at import time never crashes under SSR.
|
|
30
35
|
* Server reads/writes do not persist across requests (a later pass can back them with
|
|
31
36
|
* the request cookie for true per-user server state).
|
|
@@ -34,8 +39,8 @@ export const storage = typeof localStorage !== 'undefined' ? localStorage : crea
|
|
|
34
39
|
/**
|
|
35
40
|
* Lazy, typed accessors for the browser globals. They return the real object on the
|
|
36
41
|
* client and `undefined` on the server, so `env.window?.matchMedia(...)` is safe and
|
|
37
|
-
* typed everywhere. Deliberately
|
|
38
|
-
* would return wrong values and cause hydration mismatches
|
|
42
|
+
* typed everywhere. Deliberately not faked on the server: a stub `location`/`innerWidth`
|
|
43
|
+
* would return wrong values and cause hydration mismatches: guard with `?.` instead.
|
|
39
44
|
*/
|
|
40
45
|
export const env = {
|
|
41
46
|
get window() {
|
|
@@ -46,5 +51,5 @@ export const env = {
|
|
|
46
51
|
},
|
|
47
52
|
};
|
|
48
53
|
export function clientOnly(fn, fallback) {
|
|
49
|
-
return isClient ? fn() : fallback;
|
|
54
|
+
return isClient() ? fn() : fallback;
|
|
50
55
|
}
|
|
@@ -1 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
// src/lib/env/index.ts
|
|
2
|
+
import { isServer } from "@fluixi/dom";
|
|
3
|
+
import { isServer as onServer } from "@fluixi/dom";
|
|
4
|
+
function isClient() {
|
|
5
|
+
return !onServer();
|
|
6
|
+
}
|
|
7
|
+
function createMemoryStorage() {
|
|
8
|
+
const mem = /* @__PURE__ */ new Map();
|
|
9
|
+
return {
|
|
10
|
+
getItem: (k) => mem.has(k) ? mem.get(k) : null,
|
|
11
|
+
setItem: (k, v) => void mem.set(k, String(v)),
|
|
12
|
+
removeItem: (k) => void mem.delete(k),
|
|
13
|
+
clear: () => mem.clear(),
|
|
14
|
+
key: (i) => Array.from(mem.keys())[i] ?? null,
|
|
15
|
+
get length() {
|
|
16
|
+
return mem.size;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
var storage = typeof localStorage !== "undefined" ? localStorage : createMemoryStorage();
|
|
21
|
+
var env = {
|
|
22
|
+
get window() {
|
|
23
|
+
return typeof window !== "undefined" ? window : void 0;
|
|
24
|
+
},
|
|
25
|
+
get document() {
|
|
26
|
+
return typeof document !== "undefined" ? document : void 0;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
function clientOnly(fn, fallback) {
|
|
30
|
+
return isClient() ? fn() : fallback;
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
clientOnly,
|
|
34
|
+
createMemoryStorage,
|
|
35
|
+
env,
|
|
36
|
+
isClient,
|
|
37
|
+
isServer,
|
|
38
|
+
storage
|
|
39
|
+
};
|
|
@@ -1 +1,108 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @fluixi/core v1.0.0-alpha.84 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/lib/i18n/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
createI18n: () => createI18n,
|
|
25
|
+
detectLocale: () => detectLocale
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
var import_signal = require("@fluixi/reactive/signal");
|
|
29
|
+
var import_dom = require("@fluixi/dom");
|
|
30
|
+
var SEED_KEY = "__i18n:locale";
|
|
31
|
+
var IS_SERVER = typeof document === "undefined";
|
|
32
|
+
function readSeed() {
|
|
33
|
+
if (IS_SERVER) return void 0;
|
|
34
|
+
const data = (0, import_dom.getServerData)();
|
|
35
|
+
return data ? data[SEED_KEY] : void 0;
|
|
36
|
+
}
|
|
37
|
+
function cookiePersist(locale) {
|
|
38
|
+
if (typeof document === "undefined") return;
|
|
39
|
+
try {
|
|
40
|
+
document.cookie = `locale=${locale};path=/;max-age=31536000;samesite=lax`;
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function createI18n(config) {
|
|
45
|
+
const persist = config.persist ?? cookiePersist;
|
|
46
|
+
const [clientLocale, setClientLocale] = (0, import_signal.createSignal)(readSeed() ?? config.locale);
|
|
47
|
+
const serverCtx = () => IS_SERVER ? (0, import_dom.getRequestContext)() : void 0;
|
|
48
|
+
function locale() {
|
|
49
|
+
const ctx = serverCtx();
|
|
50
|
+
if (ctx) return ctx.__i18nLocale ?? config.locale;
|
|
51
|
+
return clientLocale();
|
|
52
|
+
}
|
|
53
|
+
function setLocale(next) {
|
|
54
|
+
const ctx = serverCtx();
|
|
55
|
+
if (ctx) {
|
|
56
|
+
ctx.__i18nLocale = next;
|
|
57
|
+
ctx.data?.set?.(SEED_KEY, next);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setClientLocale(() => next);
|
|
61
|
+
persist(next);
|
|
62
|
+
}
|
|
63
|
+
function resolve(loc, key) {
|
|
64
|
+
const bundle = config.messages[loc];
|
|
65
|
+
if (bundle == null) return void 0;
|
|
66
|
+
const flat = bundle[key];
|
|
67
|
+
if (flat !== void 0) return flat;
|
|
68
|
+
let node = bundle;
|
|
69
|
+
for (const part of key.split(".")) {
|
|
70
|
+
if (node == null || typeof node !== "object") return void 0;
|
|
71
|
+
node = node[part];
|
|
72
|
+
}
|
|
73
|
+
return node;
|
|
74
|
+
}
|
|
75
|
+
function interpolate(str, params) {
|
|
76
|
+
if (!params) return str;
|
|
77
|
+
return str.replace(/\{(\w+)\}/g, (_m, k) => k in params ? String(params[k]) : `{${k}}`);
|
|
78
|
+
}
|
|
79
|
+
function t(key, params) {
|
|
80
|
+
const loc = locale();
|
|
81
|
+
let msg = resolve(loc, key);
|
|
82
|
+
if (msg === void 0 && loc !== config.locale) msg = resolve(config.locale, key);
|
|
83
|
+
if (msg === void 0) return key;
|
|
84
|
+
if (typeof msg !== "string") {
|
|
85
|
+
const count = Number(params?.count ?? 0);
|
|
86
|
+
const category = new Intl.PluralRules(loc).select(count);
|
|
87
|
+
const branch = msg[category] ?? msg.other ?? Object.values(msg)[0] ?? "";
|
|
88
|
+
return interpolate(branch, params);
|
|
89
|
+
}
|
|
90
|
+
return interpolate(msg, params);
|
|
91
|
+
}
|
|
92
|
+
const n = (value, options) => new Intl.NumberFormat(locale(), options).format(value);
|
|
93
|
+
const d = (value, options) => new Intl.DateTimeFormat(locale(), options).format(value);
|
|
94
|
+
return { t, n, d, locale, setLocale, locales: config.locales };
|
|
95
|
+
}
|
|
96
|
+
function detectLocale(acceptLanguage, locales) {
|
|
97
|
+
if (!acceptLanguage) return void 0;
|
|
98
|
+
const ranked = acceptLanguage.split(",").map((part) => {
|
|
99
|
+
const [tag, q] = part.trim().split(";q=");
|
|
100
|
+
return { tag: tag.toLowerCase(), q: q ? parseFloat(q) : 1 };
|
|
101
|
+
}).sort((a, b) => b.q - a.q);
|
|
102
|
+
for (const { tag } of ranked) {
|
|
103
|
+
const base = tag.split("-")[0];
|
|
104
|
+
const hit = locales.find((l) => l.toLowerCase() === tag || l.toLowerCase() === base);
|
|
105
|
+
if (hit) return hit;
|
|
106
|
+
}
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fluixi/core/i18n
|
|
2
|
+
* @fluixi/core/i18n: built-in, reactive, SSR-correct internationalization.
|
|
3
3
|
*
|
|
4
4
|
* Three things make this distinct:
|
|
5
5
|
*
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
* (no flash, no hydration mismatch).
|
|
10
10
|
*
|
|
11
11
|
* 2. **No reactive-owner-context dependency.** `t()` reads the locale from the request context /
|
|
12
|
-
* module signal
|
|
12
|
+
* module signal, not `useContext`, so it resolves in lazy routes, server functions, loaders,
|
|
13
13
|
* anywhere. (This is precisely why it sidesteps the classic "useLocale() is undefined under
|
|
14
|
-
* SSR on a lazy route" failure
|
|
14
|
+
* SSR on a lazy route" failure: there's no provider owner-chain to lose.)
|
|
15
15
|
*
|
|
16
16
|
* 3. **Platform-native formatting.** Pluralization via `Intl.PluralRules`, numbers/dates via
|
|
17
|
-
* `Intl.NumberFormat`/`Intl.DateTimeFormat
|
|
17
|
+
* `Intl.NumberFormat`/`Intl.DateTimeFormat`: correct CLDR rules, zero dependencies.
|
|
18
18
|
*
|
|
19
19
|
* const i18n = createI18n({ locale: 'en', locales: ['en', 'fr'] as const, messages: { en, fr } });
|
|
20
20
|
* export const { t, n, d, locale, setLocale } = i18n;
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
* t('welcome', { name: 'Ada' }) // "Welcome, Ada"
|
|
23
23
|
* t('cart.items', { count: 3 }) // plural branch via Intl.PluralRules
|
|
24
24
|
* n(1234.5); d(new Date()); // locale-aware number/date
|
|
25
|
-
* setLocale('fr'); // reactive
|
|
25
|
+
* setLocale('fr'); // reactive, every t() re-evaluates
|
|
26
26
|
*/
|
|
27
27
|
import { type Accessor } from '@fluixi/reactive/signal';
|
|
28
|
-
/** A leaf message: a plain string, or a plural map keyed by CLDR category (`one`/`other
|
|
28
|
+
/** A leaf message: a plain string, or a plural map keyed by CLDR category (`one`/`other`/...). */
|
|
29
29
|
export type MessageValue = string | {
|
|
30
30
|
readonly [pluralCategory: string]: string;
|
|
31
31
|
};
|
|
@@ -36,13 +36,13 @@ export interface Messages {
|
|
|
36
36
|
/** CLDR plural categories. An object keyed *only* by these is a plural leaf, not a subtree. */
|
|
37
37
|
type PluralCategory = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
|
|
38
38
|
/**
|
|
39
|
-
* The union of every valid translation key in `M
|
|
39
|
+
* The union of every valid translation key in `M`: `t()` autocompletes these and flags typos.
|
|
40
40
|
* Flat keys (`'welcome'`, `'nav.dashboard'`), nested trees (`'cart.items'`, `'nested.deep.key'`),
|
|
41
|
-
* and plural maps (`{ one, other }`
|
|
41
|
+
* and plural maps (`{ one, other }` -> a leaf) all work. Only *leaf* paths are emitted, never the
|
|
42
42
|
* intermediate branch (`'nav'`, `'cart'`). Falls open to `string` when `M` is just `Messages`.
|
|
43
43
|
*
|
|
44
44
|
* A subtree and a plural map are both `Record<string, string>` structurally, so the plural leaf is
|
|
45
|
-
* detected by its keys being CLDR categories
|
|
45
|
+
* detected by its keys being CLDR categories, not by "all children are strings" (which wrongly
|
|
46
46
|
* truncated `nav: { dashboard, settings }` to `'nav'`).
|
|
47
47
|
*/
|
|
48
48
|
export type MessageKey<M> = M extends object ? {
|
|
@@ -65,13 +65,34 @@ export interface I18n<L extends string, K extends string = string> {
|
|
|
65
65
|
n: (value: number, options?: Intl.NumberFormatOptions) => string;
|
|
66
66
|
/** Format a date for the active locale (`Intl.DateTimeFormat`). */
|
|
67
67
|
d: (value: Date | number, options?: Intl.DateTimeFormatOptions) => string;
|
|
68
|
-
/** The active locale
|
|
68
|
+
/** The active locale: reactive on the client, per-request on the server. */
|
|
69
69
|
locale: Accessor<L>;
|
|
70
70
|
/** Switch the active locale (client: reactive + persisted; server: this request only). */
|
|
71
71
|
setLocale: (locale: L) => void;
|
|
72
72
|
/** Supported locales. */
|
|
73
73
|
locales: readonly L[];
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Translations, with the message keys inferred from the messages themselves.
|
|
77
|
+
*
|
|
78
|
+
* `t()` accepts only leaf paths that exist in the catalogue, so a typo is a type error
|
|
79
|
+
* rather than a string echoed back at a visitor. A missing key at runtime returns the key,
|
|
80
|
+
* which is visible in the page and traceable.
|
|
81
|
+
*
|
|
82
|
+
* The locale is per request on the server and reactive on the client, seeded from what the
|
|
83
|
+
* server resolved so hydration cannot disagree about the language. Persistence is a cookie
|
|
84
|
+
* by default, which is the only store a server render can read.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* const i18n = createI18n({
|
|
89
|
+
* locale: 'en',
|
|
90
|
+
* messages: { en: { hi: 'Hello' }, fr: { hi: 'Bonjour' } },
|
|
91
|
+
* });
|
|
92
|
+
* i18n.t('hi'); // 'Hello'
|
|
93
|
+
* i18n.setLocale('fr');
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
75
96
|
export declare function createI18n<L extends string, const M extends Record<L, Messages>>(config: I18nConfig<L, M>): I18n<L, MessageKey<M[keyof M]>>;
|
|
76
97
|
/** Pick the best supported locale from an `Accept-Language` header, else `undefined`. */
|
|
77
98
|
export declare function detectLocale<L extends string>(acceptLanguage: string | null | undefined, locales: readonly L[]): L | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/i18n/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGtE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/i18n/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGtE,kGAAkG;AAClG,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAClF,wCAAwC;AACxC,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,QAAQ,CAAC;CACjD;AAED,+FAA+F;AAC/F,KAAK,cAAc,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAExE;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,GACxC;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GACxC,CAAC,GACD,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,GACnC,CAAC,GACD,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GACjB,GAAG,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAC1B,CAAC;CACV,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GACnB,MAAM,CAAC;AAEX,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC;IAC/F,iCAAiC;IACjC,MAAM,EAAE,CAAC,CAAC;IACV,yBAAyB;IACzB,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IACtB,mGAAmG;IACnG,QAAQ,EAAE,CAAC,CAAC;IACZ,qFAAqF;IACrF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM;IAC/D,mGAAmG;IACnG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IACxE,mEAAmE;IACnE,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,KAAK,MAAM,CAAC;IACjE,mEAAmE;IACnE,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,qBAAqB,KAAK,MAAM,CAAC;IAC1E,4EAA4E;IAC5E,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpB,0FAA0F;IAC1F,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAC/B,yBAAyB;IACzB,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;CACvB;AAoBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,EAC9E,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GACvB,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAiEjC;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACzC,OAAO,EAAE,SAAS,CAAC,EAAE,GACpB,CAAC,GAAG,SAAS,CAef"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fluixi/core/i18n
|
|
2
|
+
* @fluixi/core/i18n: built-in, reactive, SSR-correct internationalization.
|
|
3
3
|
*
|
|
4
4
|
* Three things make this distinct:
|
|
5
5
|
*
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
* (no flash, no hydration mismatch).
|
|
10
10
|
*
|
|
11
11
|
* 2. **No reactive-owner-context dependency.** `t()` reads the locale from the request context /
|
|
12
|
-
* module signal
|
|
12
|
+
* module signal, not `useContext`, so it resolves in lazy routes, server functions, loaders,
|
|
13
13
|
* anywhere. (This is precisely why it sidesteps the classic "useLocale() is undefined under
|
|
14
|
-
* SSR on a lazy route" failure
|
|
14
|
+
* SSR on a lazy route" failure: there's no provider owner-chain to lose.)
|
|
15
15
|
*
|
|
16
16
|
* 3. **Platform-native formatting.** Pluralization via `Intl.PluralRules`, numbers/dates via
|
|
17
|
-
* `Intl.NumberFormat`/`Intl.DateTimeFormat
|
|
17
|
+
* `Intl.NumberFormat`/`Intl.DateTimeFormat`: correct CLDR rules, zero dependencies.
|
|
18
18
|
*
|
|
19
19
|
* const i18n = createI18n({ locale: 'en', locales: ['en', 'fr'] as const, messages: { en, fr } });
|
|
20
20
|
* export const { t, n, d, locale, setLocale } = i18n;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* t('welcome', { name: 'Ada' }) // "Welcome, Ada"
|
|
23
23
|
* t('cart.items', { count: 3 }) // plural branch via Intl.PluralRules
|
|
24
24
|
* n(1234.5); d(new Date()); // locale-aware number/date
|
|
25
|
-
* setLocale('fr'); // reactive
|
|
25
|
+
* setLocale('fr'); // reactive, every t() re-evaluates
|
|
26
26
|
*/
|
|
27
27
|
import { createSignal } from '@fluixi/reactive/signal';
|
|
28
28
|
import { getRequestContext, getServerData } from '@fluixi/dom';
|
|
@@ -41,9 +41,30 @@ function cookiePersist(locale) {
|
|
|
41
41
|
document.cookie = `locale=${locale};path=/;max-age=31536000;samesite=lax`;
|
|
42
42
|
}
|
|
43
43
|
catch {
|
|
44
|
-
/* private mode / disabled
|
|
44
|
+
/* private mode / disabled: best effort */
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Translations, with the message keys inferred from the messages themselves.
|
|
49
|
+
*
|
|
50
|
+
* `t()` accepts only leaf paths that exist in the catalogue, so a typo is a type error
|
|
51
|
+
* rather than a string echoed back at a visitor. A missing key at runtime returns the key,
|
|
52
|
+
* which is visible in the page and traceable.
|
|
53
|
+
*
|
|
54
|
+
* The locale is per request on the server and reactive on the client, seeded from what the
|
|
55
|
+
* server resolved so hydration cannot disagree about the language. Persistence is a cookie
|
|
56
|
+
* by default, which is the only store a server render can read.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const i18n = createI18n({
|
|
61
|
+
* locale: 'en',
|
|
62
|
+
* messages: { en: { hi: 'Hello' }, fr: { hi: 'Bonjour' } },
|
|
63
|
+
* });
|
|
64
|
+
* i18n.t('hi'); // 'Hello'
|
|
65
|
+
* i18n.setLocale('fr');
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
47
68
|
export function createI18n(config) {
|
|
48
69
|
const persist = config.persist ?? cookiePersist;
|
|
49
70
|
// Client: a reactive signal seeded from the server-resolved locale (so hydration matches).
|
|
@@ -52,14 +73,14 @@ export function createI18n(config) {
|
|
|
52
73
|
function locale() {
|
|
53
74
|
const ctx = serverCtx();
|
|
54
75
|
if (ctx)
|
|
55
|
-
return ctx.__i18nLocale ?? config.locale; // server: per-request, no shared signal
|
|
76
|
+
return ctx.__i18nLocale ?? config.locale; // server: per-request, no shared signal -> no leak
|
|
56
77
|
return clientLocale(); // client: reactive
|
|
57
78
|
}
|
|
58
79
|
function setLocale(next) {
|
|
59
80
|
const ctx = serverCtx();
|
|
60
81
|
if (ctx) {
|
|
61
82
|
ctx.__i18nLocale = next;
|
|
62
|
-
ctx.data?.set?.(SEED_KEY, next); // serialize
|
|
83
|
+
ctx.data?.set?.(SEED_KEY, next); // serialize -> client hydrates in the same language
|
|
63
84
|
return;
|
|
64
85
|
}
|
|
65
86
|
setClientLocale(() => next);
|
|
@@ -69,7 +90,7 @@ export function createI18n(config) {
|
|
|
69
90
|
const bundle = config.messages[loc];
|
|
70
91
|
if (bundle == null)
|
|
71
92
|
return undefined;
|
|
72
|
-
// Flat key first
|
|
93
|
+
// Flat key first: supports dotted flat keys like `nav.dashboard` (not just nested trees).
|
|
73
94
|
const flat = bundle[key];
|
|
74
95
|
if (flat !== undefined)
|
|
75
96
|
return flat;
|
|
@@ -93,7 +114,7 @@ export function createI18n(config) {
|
|
|
93
114
|
if (msg === undefined && loc !== config.locale)
|
|
94
115
|
msg = resolve(config.locale, key); // fallback locale
|
|
95
116
|
if (msg === undefined)
|
|
96
|
-
return key; // missing
|
|
117
|
+
return key; // missing -> echo the key (visible, debuggable)
|
|
97
118
|
if (typeof msg !== 'string') {
|
|
98
119
|
const count = Number(params?.count ?? 0);
|
|
99
120
|
const category = new Intl.PluralRules(loc).select(count);
|
|
@@ -1 +1,88 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @fluixi/core v1.0.0-alpha.84 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
|
|
3
|
+
// src/lib/i18n/index.ts
|
|
4
|
+
import { createSignal } from "@fluixi/reactive/signal";
|
|
5
|
+
import { getRequestContext, getServerData } from "@fluixi/dom";
|
|
6
|
+
var SEED_KEY = "__i18n:locale";
|
|
7
|
+
var IS_SERVER = typeof document === "undefined";
|
|
8
|
+
function readSeed() {
|
|
9
|
+
if (IS_SERVER) return void 0;
|
|
10
|
+
const data = getServerData();
|
|
11
|
+
return data ? data[SEED_KEY] : void 0;
|
|
12
|
+
}
|
|
13
|
+
function cookiePersist(locale) {
|
|
14
|
+
if (typeof document === "undefined") return;
|
|
15
|
+
try {
|
|
16
|
+
document.cookie = `locale=${locale};path=/;max-age=31536000;samesite=lax`;
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function createI18n(config) {
|
|
21
|
+
const persist = config.persist ?? cookiePersist;
|
|
22
|
+
const [clientLocale, setClientLocale] = createSignal(readSeed() ?? config.locale);
|
|
23
|
+
const serverCtx = () => IS_SERVER ? getRequestContext() : void 0;
|
|
24
|
+
function locale() {
|
|
25
|
+
const ctx = serverCtx();
|
|
26
|
+
if (ctx) return ctx.__i18nLocale ?? config.locale;
|
|
27
|
+
return clientLocale();
|
|
28
|
+
}
|
|
29
|
+
function setLocale(next) {
|
|
30
|
+
const ctx = serverCtx();
|
|
31
|
+
if (ctx) {
|
|
32
|
+
ctx.__i18nLocale = next;
|
|
33
|
+
ctx.data?.set?.(SEED_KEY, next);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
setClientLocale(() => next);
|
|
37
|
+
persist(next);
|
|
38
|
+
}
|
|
39
|
+
function resolve(loc, key) {
|
|
40
|
+
const bundle = config.messages[loc];
|
|
41
|
+
if (bundle == null) return void 0;
|
|
42
|
+
const flat = bundle[key];
|
|
43
|
+
if (flat !== void 0) return flat;
|
|
44
|
+
let node = bundle;
|
|
45
|
+
for (const part of key.split(".")) {
|
|
46
|
+
if (node == null || typeof node !== "object") return void 0;
|
|
47
|
+
node = node[part];
|
|
48
|
+
}
|
|
49
|
+
return node;
|
|
50
|
+
}
|
|
51
|
+
function interpolate(str, params) {
|
|
52
|
+
if (!params) return str;
|
|
53
|
+
return str.replace(/\{(\w+)\}/g, (_m, k) => k in params ? String(params[k]) : `{${k}}`);
|
|
54
|
+
}
|
|
55
|
+
function t(key, params) {
|
|
56
|
+
const loc = locale();
|
|
57
|
+
let msg = resolve(loc, key);
|
|
58
|
+
if (msg === void 0 && loc !== config.locale) msg = resolve(config.locale, key);
|
|
59
|
+
if (msg === void 0) return key;
|
|
60
|
+
if (typeof msg !== "string") {
|
|
61
|
+
const count = Number(params?.count ?? 0);
|
|
62
|
+
const category = new Intl.PluralRules(loc).select(count);
|
|
63
|
+
const branch = msg[category] ?? msg.other ?? Object.values(msg)[0] ?? "";
|
|
64
|
+
return interpolate(branch, params);
|
|
65
|
+
}
|
|
66
|
+
return interpolate(msg, params);
|
|
67
|
+
}
|
|
68
|
+
const n = (value, options) => new Intl.NumberFormat(locale(), options).format(value);
|
|
69
|
+
const d = (value, options) => new Intl.DateTimeFormat(locale(), options).format(value);
|
|
70
|
+
return { t, n, d, locale, setLocale, locales: config.locales };
|
|
71
|
+
}
|
|
72
|
+
function detectLocale(acceptLanguage, locales) {
|
|
73
|
+
if (!acceptLanguage) return void 0;
|
|
74
|
+
const ranked = acceptLanguage.split(",").map((part) => {
|
|
75
|
+
const [tag, q] = part.trim().split(";q=");
|
|
76
|
+
return { tag: tag.toLowerCase(), q: q ? parseFloat(q) : 1 };
|
|
77
|
+
}).sort((a, b) => b.q - a.q);
|
|
78
|
+
for (const { tag } of ranked) {
|
|
79
|
+
const base = tag.split("-")[0];
|
|
80
|
+
const hit = locales.find((l) => l.toLowerCase() === tag || l.toLowerCase() === base);
|
|
81
|
+
if (hit) return hit;
|
|
82
|
+
}
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
createI18n,
|
|
87
|
+
detectLocale
|
|
88
|
+
};
|