@yozora-tech/qfluent 0.1.1 → 0.1.2

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.
@@ -1,173 +1,177 @@
1
- import { createContextId, useContext, componentQrl, inlinedQrl, useSignal, useContextProvider, useOnDocument, _captures, useTaskQrl, noSerialize, isServer, _jsxSorted, Slot } from "@qwik.dev/core";
1
+ import { Slot, _captures, _jsxSorted, componentQrl, createContextId, inlinedQrl, isServer, noSerialize, useContext, useContextProvider, useOnDocument, useSignal, useTaskQrl } from "@qwik.dev/core";
2
2
  import { FluentBundle, FluentResource } from "@fluent/bundle";
3
- const FluentLocalesId = createContextId("QwikFluentLocales");
4
- const FluentResourcesId = createContextId("QwikFluentResources");
5
- const FluentBundlesId = createContextId("QwikFluentBundles");
6
- const FluentErrorsId = createContextId("QwikFluentErrors");
7
- function useFluentLocales() {
8
- return useContext(FluentLocalesId);
3
+ /** @internal */ var FluentLocalesId = createContextId("QwikFluentLocales");
4
+ /** @internal */ var FluentResourcesId = createContextId("QwikFluentResources");
5
+ /** @internal */ var FluentBundlesId = createContextId("QwikFluentBundles");
6
+ /** @internal */ var FluentErrorsId = createContextId("QwikFluentErrors");
7
+ /**
8
+ * Signal to list of preferred locales. Translations are picked from these languages, with fallbacks as needed.
9
+ *
10
+ * Normally you want to set this to `preferred_languages.concat(navigator.languages)`.
11
+ *
12
+ * ```tsx
13
+ * const locales = useFluentLocales();
14
+ * return (
15
+ * <select onChange$={(event, select) => {
16
+ * locales.value = [select.value].concat(navigator.languages);
17
+ * }}>
18
+ * <option value="ar">Arabic</option>
19
+ * <option value="en">English</option>
20
+ * <option value="ja">Japanese</option>
21
+ * ...
22
+ * </select>
23
+ * );
24
+ * ```
25
+ */ function useFluentLocales() {
26
+ return useContext(FluentLocalesId);
9
27
  }
10
- function useFluentResources() {
11
- return useContext(FluentResourcesId);
28
+ /**
29
+ * Includes your resources and compiled FluentBundles by locales.
30
+ *
31
+ * You can make use of your custom resource properties with this.
32
+ */ function useFluentResources() {
33
+ return useContext(FluentResourcesId);
12
34
  }
13
- function useFluentErrors() {
14
- return useContext(FluentErrorsId);
35
+ /**
36
+ * Useful for debugging errors in resources.
37
+ *
38
+ * You can clear the errors after use.
39
+ */ function useFluentErrors() {
40
+ return useContext(FluentErrorsId);
15
41
  }
16
42
  function ataToUtf8(pathname) {
17
- const label = ";base64,";
18
- const a = pathname.indexOf(label);
19
- if (a === -1) {
20
- throw new Error("Invalid ata url pathname: " + pathname);
21
- }
22
- const binaryString = atob(pathname.slice(a + label.length));
23
- const bytes = new Uint8Array(binaryString.length);
24
- for (let i = 0; i < binaryString.length; i++) {
25
- bytes[i] = binaryString.charCodeAt(i);
26
- }
27
- return new TextDecoder().decode(bytes);
43
+ const a = pathname.indexOf(";base64,");
44
+ if (a === -1) throw new Error("Invalid ata url pathname: " + pathname);
45
+ const binaryString = atob(pathname.slice(a + 8));
46
+ const bytes = new Uint8Array(binaryString.length);
47
+ for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i);
48
+ return new TextDecoder().decode(bytes);
28
49
  }
29
- const FluentProvider = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((_rawProps) => {
30
- const locales = useSignal(_rawProps.acceptLanguage ?? [
31
- "en"
32
- ]);
33
- const bunmap = useSignal({});
34
- const resmap = useSignal({});
35
- const errors = useSignal([]);
36
- useContextProvider(FluentLocalesId, locales);
37
- useContextProvider(FluentBundlesId, bunmap);
38
- useContextProvider(FluentResourcesId, resmap);
39
- useContextProvider(FluentErrorsId, errors);
40
- const ssr = useSignal(true);
41
- useOnDocument("load", /* @__PURE__ */ inlinedQrl(() => {
42
- const locales2 = _captures[0], ssr2 = _captures[1];
43
- if (!ssr2.value) return;
44
- ssr2.value = false;
45
- locales2.value = navigator.languages.concat();
46
- }, "FluentProvider_component_useOnDocument_9BL0L0WgLlY", [
47
- locales,
48
- ssr
49
- ]));
50
- useTaskQrl(/* @__PURE__ */ inlinedQrl((_rawProps1) => {
51
- const _rawProps2 = _captures[0], bunmap2 = _captures[1], errors2 = _captures[2], locales2 = _captures[3], resmap2 = _captures[4], ssr2 = _captures[5];
52
- _rawProps1.track(locales2);
53
- _rawProps1.track(ssr2);
54
- if (!bunmap2.value) {
55
- bunmap2.value = {};
56
- }
57
- if (!resmap2.value) {
58
- resmap2.value = {};
59
- }
60
- if (!errors2.value) {
61
- errors2.value = [];
62
- }
63
- const res = noSerialize(resmap2.value);
64
- const bun = noSerialize(bunmap2.value);
65
- const promises = [];
66
- const whitelist = locales2.value;
67
- let has_errors = false;
68
- for (const r of _rawProps2.resources) {
69
- let ok = false;
70
- for (const x of r.locales) {
71
- if (res[x]) {
72
- break;
73
- }
74
- if (whitelist.includes(x)) {
75
- ok = true;
76
- break;
77
- }
78
- }
79
- if (!ok) {
80
- continue;
81
- }
82
- const b = new FluentBundle(r.locales);
83
- for (const x of r.locales) {
84
- res[x] = r;
85
- bun[x] = b;
86
- }
87
- for (const url of r.resources) {
88
- let promise;
89
- if (isServer) {
90
- let path = url.toString().slice(1);
91
- {
92
- path = new URL(path, new URL("../dist/", _rawProps2.origin));
93
- }
94
- if (path.protocol === "ata:") {
95
- promise = new Promise((resolve, reject) => {
96
- try {
97
- resolve(ataToUtf8(path.pathname));
98
- } catch (err) {
99
- reject(err);
100
- }
101
- });
102
- } else {
103
- promise = import("node:fs/promises").then((fs) => fs.readFile(path.pathname)).then((buf) => new TextDecoder().decode(buf));
104
- }
105
- } else {
106
- promise = fetch(url).then((resp) => resp.text());
107
- }
108
- promises.push(promise.then((s) => {
109
- const errset = b.addResource(new FluentResource(s));
110
- if (errset.length) {
111
- errors2.value.push({
112
- resource: r,
113
- errors: errset
114
- });
115
- has_errors = true;
116
- console.error(`[qfluent] Errors in resource ${url}:`, errset);
117
- }
118
- }));
119
- }
120
- }
121
- return Promise.all(promises).then(() => {
122
- bunmap2.trigger();
123
- resmap2.trigger();
124
- if (has_errors) {
125
- errors2.trigger();
126
- }
127
- });
128
- }, "FluentProvider_component_useTask_0r0LUJYLJnU", [
129
- _rawProps,
130
- bunmap,
131
- errors,
132
- locales,
133
- resmap,
134
- ssr
135
- ]));
136
- useTaskQrl(/* @__PURE__ */ inlinedQrl(({ track }) => {
137
- const locales2 = _captures[0], resmap2 = _captures[1], ssr2 = _captures[2];
138
- if (track(ssr2)) return;
139
- if (!track(resmap2)) return;
140
- track(locales2);
141
- const root = document.querySelector("html");
142
- if (!root) return;
143
- for (const l of locales2.value) {
144
- const b = resmap2.value[l];
145
- if (!b) {
146
- continue;
147
- }
148
- root.lang = l;
149
- const dir = b.dir;
150
- if (dir) {
151
- root.dir = dir;
152
- } else {
153
- root.dir = "";
154
- }
155
- break;
156
- }
157
- }, "FluentProvider_component_useTask_1_GdWGQNUVI4g", [
158
- locales,
159
- resmap,
160
- ssr
161
- ]));
162
- return /* @__PURE__ */ _jsxSorted(Slot, null, null, null, 3, "76_0");
50
+ /**
51
+ * Use this in `src/root.tsx`.
52
+ *
53
+ * ```tsx
54
+ * <body>
55
+ * <FluentProvider resources={...}>
56
+ * <RouterOutlet />
57
+ * </FluentProvider>
58
+ * </body>
59
+ * ```
60
+ */ var FluentProvider = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl((_rawProps) => {
61
+ const locales = useSignal(_rawProps.acceptLanguage ?? ["en"]);
62
+ const bunmap = useSignal(noSerialize({}));
63
+ const resmap = useSignal(noSerialize({}));
64
+ const errors = useSignal(noSerialize([]));
65
+ useContextProvider(FluentLocalesId, locales);
66
+ useContextProvider(FluentBundlesId, bunmap);
67
+ useContextProvider(FluentResourcesId, resmap);
68
+ useContextProvider(FluentErrorsId, errors);
69
+ const ssr = useSignal(true);
70
+ useOnDocument("load", /*#__PURE__*/ inlinedQrl(() => {
71
+ const locales = _captures[0], ssr = _captures[1];
72
+ if (!ssr.value) return;
73
+ ssr.value = false;
74
+ locales.value = navigator.languages.concat();
75
+ }, "FluentProvider_component_useOnDocument_9BL0L0WgLlY", [locales, ssr]));
76
+ useTaskQrl(/*#__PURE__*/ inlinedQrl((_rawProps1) => {
77
+ const _rawProps = _captures[0], bunmap = _captures[1], errors = _captures[2], locales = _captures[3], resmap = _captures[4], ssr = _captures[5];
78
+ _rawProps1.track(locales);
79
+ _rawProps1.track(ssr);
80
+ if (!bunmap.value) bunmap.value = noSerialize({});
81
+ if (!resmap.value) resmap.value = noSerialize({});
82
+ if (!errors.value) errors.value = noSerialize([]);
83
+ const res = noSerialize(resmap.value);
84
+ const bun = noSerialize(bunmap.value);
85
+ const promises = [];
86
+ const whitelist = locales.value;
87
+ let has_errors = false;
88
+ for (const r of _rawProps.resources) {
89
+ let ok = false;
90
+ for (const x of r.locales) {
91
+ if (res[x]) break;
92
+ if (whitelist.includes(x)) {
93
+ ok = true;
94
+ break;
95
+ }
96
+ }
97
+ if (!ok) continue;
98
+ const b = new FluentBundle(r.locales);
99
+ for (const x of r.locales) {
100
+ res[x] = r;
101
+ bun[x] = b;
102
+ }
103
+ for (const url of r.resources) {
104
+ let promise;
105
+ if (isServer) {
106
+ const rel = url.toString().slice(1);
107
+ const candidates = [new URL("../dist/", _rawProps.origin), _rawProps.origin].map((base) => new URL(rel, base));
108
+ const ata = candidates.find((c) => c.protocol === "ata:");
109
+ if (ata) promise = new Promise((resolve, reject) => {
110
+ try {
111
+ resolve(ataToUtf8(ata.pathname));
112
+ } catch (err) {
113
+ reject(err);
114
+ }
115
+ });
116
+ else promise = import("node:fs/promises").then(async (fs) => {
117
+ const { fileURLToPath } = await import("node:url");
118
+ let firstError;
119
+ for (const c of candidates) try {
120
+ const buf = await fs.readFile(c.protocol === "file:" ? fileURLToPath(c) : c.pathname);
121
+ return new TextDecoder().decode(buf);
122
+ } catch (err) {
123
+ if (firstError === void 0) firstError = err;
124
+ }
125
+ throw firstError;
126
+ });
127
+ } else promise = fetch(url).then((resp) => resp.text());
128
+ promises.push(promise.then((s) => {
129
+ const errset = b.addResource(new FluentResource(s));
130
+ if (errset.length) {
131
+ errors.value.push({
132
+ resource: r,
133
+ errors: errset
134
+ });
135
+ has_errors = true;
136
+ console.error(`[qfluent] Errors in resource ${url}:`, errset);
137
+ }
138
+ }));
139
+ }
140
+ }
141
+ return Promise.all(promises).then(() => {
142
+ bunmap.trigger();
143
+ resmap.trigger();
144
+ if (has_errors) errors.trigger();
145
+ });
146
+ }, "FluentProvider_component_useTask_0r0LUJYLJnU", [
147
+ _rawProps,
148
+ bunmap,
149
+ errors,
150
+ locales,
151
+ resmap,
152
+ ssr
153
+ ]));
154
+ useTaskQrl(/*#__PURE__*/ inlinedQrl(({ track }) => {
155
+ const locales = _captures[0], resmap = _captures[1], ssr = _captures[2];
156
+ if (track(ssr)) return;
157
+ if (!track(resmap)) return;
158
+ track(locales);
159
+ const root = document.querySelector("html");
160
+ if (!root) return;
161
+ for (const l of locales.value) {
162
+ const b = resmap.value[l];
163
+ if (!b) continue;
164
+ root.lang = l;
165
+ const dir = b.dir;
166
+ if (dir) root.dir = dir;
167
+ else root.dir = "";
168
+ break;
169
+ }
170
+ }, "FluentProvider_component_useTask_1_GdWGQNUVI4g", [
171
+ locales,
172
+ resmap,
173
+ ssr
174
+ ]));
175
+ return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 3, "76_0");
163
176
  }, "FluentProvider_component_LWfKAJ3jVqM"));
164
- export {
165
- FluentBundlesId,
166
- FluentErrorsId,
167
- FluentLocalesId,
168
- FluentProvider,
169
- FluentResourcesId,
170
- useFluentErrors,
171
- useFluentLocales,
172
- useFluentResources
173
- };
177
+ export { FluentBundlesId, FluentErrorsId, FluentLocalesId, FluentProvider, FluentResourcesId, useFluentErrors, useFluentLocales, useFluentResources };
@@ -1,13 +1,18 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- function parseAcceptLanguage(header) {
4
- return header.split(",").map((entry) => {
5
- const [locale, ...params] = entry.split(";");
6
- const q = params.find((p) => p.trim().startsWith("q="));
7
- return {
8
- locale: locale.trim(),
9
- q: q ? parseFloat(q.split("=")[1]) : 1
10
- };
11
- }).filter((x) => x.locale).sort((a, b) => b.q - a.q).map((x) => x.locale);
1
+ /**
2
+ * Parse an `Accept-Language` header as array of locales, sorted by quality value.
3
+ *
4
+ * ```ts
5
+ * parseAcceptLanguage("en-US;q=0.3, ja, zh-CN;q=0.9, zh;q=0.7")
6
+ * // [ "ja", "zh-CN", "zh", "en-US" ]
7
+ * ```
8
+ */ function parseAcceptLanguage(header) {
9
+ return header.split(",").map((entry) => {
10
+ const [locale, ...params] = entry.split(";");
11
+ const q = params.find((p) => p.trim().startsWith("q="));
12
+ return {
13
+ locale: locale.trim(),
14
+ q: q ? parseFloat(q.split("=")[1]) : 1
15
+ };
16
+ }).filter((x) => x.locale).sort((a, b) => b.q - a.q).map((x) => x.locale);
12
17
  }
13
18
  exports.parseAcceptLanguage = parseAcceptLanguage;
@@ -1,13 +1,18 @@
1
- function parseAcceptLanguage(header) {
2
- return header.split(",").map((entry) => {
3
- const [locale, ...params] = entry.split(";");
4
- const q = params.find((p) => p.trim().startsWith("q="));
5
- return {
6
- locale: locale.trim(),
7
- q: q ? parseFloat(q.split("=")[1]) : 1
8
- };
9
- }).filter((x) => x.locale).sort((a, b) => b.q - a.q).map((x) => x.locale);
1
+ /**
2
+ * Parse an `Accept-Language` header as array of locales, sorted by quality value.
3
+ *
4
+ * ```ts
5
+ * parseAcceptLanguage("en-US;q=0.3, ja, zh-CN;q=0.9, zh;q=0.7")
6
+ * // [ "ja", "zh-CN", "zh", "en-US" ]
7
+ * ```
8
+ */ function parseAcceptLanguage(header) {
9
+ return header.split(",").map((entry) => {
10
+ const [locale, ...params] = entry.split(";");
11
+ const q = params.find((p) => p.trim().startsWith("q="));
12
+ return {
13
+ locale: locale.trim(),
14
+ q: q ? parseFloat(q.split("=")[1]) : 1
15
+ };
16
+ }).filter((x) => x.locale).sort((a, b) => b.q - a.q).map((x) => x.locale);
10
17
  }
11
- export {
12
- parseAcceptLanguage
13
- };
18
+ export { parseAcceptLanguage };
@@ -1,87 +1,86 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const core = require("@qwik.dev/core");
4
- const context = require("./context.qwik.cjs");
1
+ const require_context = require("./context.qwik.cjs");
2
+ let _qwik_dev_core = require("@qwik.dev/core");
5
3
  function wrapArgs(args) {
6
- switch (typeof args) {
7
- case "string":
8
- case "number":
9
- case "bigint":
10
- case "boolean":
11
- return {
12
- x: args
13
- };
14
- case "undefined":
15
- return {
16
- x: "undefined"
17
- };
18
- case "object":
19
- if (args === null) {
20
- return {
21
- x: "null"
22
- };
23
- }
24
- return args;
25
- default:
26
- return {
27
- x: String(args)
28
- };
29
- }
4
+ switch (typeof args) {
5
+ case "string":
6
+ case "number":
7
+ case "bigint":
8
+ case "boolean": return { x: args };
9
+ case "undefined": return { x: "undefined" };
10
+ case "object":
11
+ if (args === null) return { x: "null" };
12
+ return args;
13
+ default: return { x: String(args) };
14
+ }
30
15
  }
31
16
  function dummyT(s, args) {
32
- return s + (args ? " + " + args : "");
17
+ return s + (args ? " + " + args : "");
33
18
  }
34
- function useT(s, args) {
35
- const locales = context.useFluentLocales();
36
- const bundles = core.useContext(context.FluentBundlesId);
37
- const message = core.useComputedQrl(/* @__PURE__ */ core.inlinedQrl(() => {
38
- const args2 = core._captures[0], bundles2 = core._captures[1], locales2 = core._captures[2], s2 = core._captures[3];
39
- const value = args2?.value;
40
- const placeholder = dummyT(s2, value);
41
- const wargs = wrapArgs(value);
42
- if (!bundles2.value) {
43
- return placeholder;
44
- }
45
- for (const k of locales2.value) {
46
- const b = bundles2.value[k];
47
- if (!b) {
48
- continue;
49
- }
50
- const m = b.getMessage(s2);
51
- if (!m || !m.value) {
52
- continue;
53
- }
54
- return b.formatPattern(m.value, wargs);
55
- }
56
- return placeholder;
57
- }, "useT_message_useComputed_S0G7htNeJXI", [
58
- args,
59
- bundles,
60
- locales,
61
- s
62
- ]));
63
- return message;
19
+ /**
20
+ * Return a reactive signal to a translated string.
21
+ *
22
+ * Useful for non-DOM use cases (like `document.title`, see `useLocalizedTitle`).
23
+ *
24
+ * ```ts
25
+ * const t = useT("my-title");
26
+ * useTask$(({ track }) => {
27
+ * track(t);
28
+ * document.title = t.value;
29
+ * });
30
+ * ```
31
+ */ function useT(s, args) {
32
+ const locales = require_context.useFluentLocales();
33
+ const bundles = (0, _qwik_dev_core.useContext)(require_context.FluentBundlesId);
34
+ return (0, _qwik_dev_core.useComputedQrl)(/*#__PURE__*/ (0, _qwik_dev_core.inlinedQrl)(() => {
35
+ const args = _qwik_dev_core._captures[0], bundles = _qwik_dev_core._captures[1], locales = _qwik_dev_core._captures[2], s = _qwik_dev_core._captures[3];
36
+ const value = args?.value;
37
+ const placeholder = dummyT(s, value);
38
+ const wargs = wrapArgs(value);
39
+ if (!bundles.value) return placeholder;
40
+ for (const k of locales.value) {
41
+ const b = bundles.value[k];
42
+ if (!b) continue;
43
+ const m = b.getMessage(s);
44
+ if (!m || !m.value) continue;
45
+ return b.formatPattern(m.value, wargs);
46
+ }
47
+ return placeholder;
48
+ }, "useT_message_useComputed_S0G7htNeJXI", [
49
+ args,
50
+ bundles,
51
+ locales,
52
+ s
53
+ ]));
64
54
  }
65
- const T = /* @__PURE__ */ core.componentQrl(/* @__PURE__ */ core.inlinedQrl((_rawProps) => {
66
- return useT(_rawProps.s, _rawProps.args).value;
55
+ /**
56
+ * Translate a string in TSX idiom.
57
+ *
58
+ * Pass a signal for `args`.
59
+ *
60
+ * ```tsx
61
+ * const x = useSignal(1);
62
+ * return <T s="x-apples" args={x} />;
63
+ * ```
64
+ */ var T = /*#__PURE__*/ (0, _qwik_dev_core.componentQrl)(/*#__PURE__*/ (0, _qwik_dev_core.inlinedQrl)((_rawProps) => {
65
+ return useT(_rawProps.s, _rawProps.args).value;
67
66
  }, "T_component_g8a5BXhl0hY"));
68
- function useLocalizedTitle(s, args) {
69
- const t = useT(s, args);
70
- core.useOnDocument("qidle", /* @__PURE__ */ core.inlinedQrl(() => {
71
- const t2 = core._captures[0];
72
- if (typeof document === "undefined") return;
73
- document.title = t2.value;
74
- }, "useLocalizedTitle_useOnDocument_VXsb0xE3U2c", [
75
- t
76
- ]));
77
- core.useTaskQrl(/* @__PURE__ */ core.inlinedQrl(({ track }) => {
78
- const t2 = core._captures[0];
79
- track(t2);
80
- if (typeof document === "undefined") return;
81
- document.title = t2.value;
82
- }, "useLocalizedTitle_useTask_MxJmwSF2dkE", [
83
- t
84
- ]));
67
+ /**
68
+ * Make the document title (`<title>` element in `<head>`) translated, and follow locale changes.
69
+ *
70
+ * Note: Only effective after resumption on clients. Title generated during SSG is untouched.
71
+ */ function useLocalizedTitle(s, args) {
72
+ const t = useT(s, args);
73
+ (0, _qwik_dev_core.useOnDocument)("qidle", /*#__PURE__*/ (0, _qwik_dev_core.inlinedQrl)(() => {
74
+ const t = _qwik_dev_core._captures[0];
75
+ if (typeof document === "undefined") return;
76
+ document.title = t.value;
77
+ }, "useLocalizedTitle_useOnDocument_VXsb0xE3U2c", [t]));
78
+ (0, _qwik_dev_core.useTaskQrl)(/*#__PURE__*/ (0, _qwik_dev_core.inlinedQrl)(({ track }) => {
79
+ const t = _qwik_dev_core._captures[0];
80
+ track(t);
81
+ if (typeof document === "undefined") return;
82
+ document.title = t.value;
83
+ }, "useLocalizedTitle_useTask_MxJmwSF2dkE", [t]));
85
84
  }
86
85
  exports.T = T;
87
86
  exports.useLocalizedTitle = useLocalizedTitle;