intor 2.2.5 → 2.2.7
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/config/index.cjs +10 -10
- package/dist/config/index.js +10 -10
- package/dist/index.cjs +21 -583
- package/dist/index.d.cts +28 -260
- package/dist/index.d.ts +28 -260
- package/dist/index.js +20 -574
- package/dist/next/index.cjs +247 -261
- package/dist/next/index.d.cts +102 -103
- package/dist/next/index.d.ts +102 -103
- package/dist/next/index.js +246 -259
- package/dist/next/server/index.cjs +26 -18
- package/dist/next/server/index.d.cts +79 -8
- package/dist/next/server/index.d.ts +79 -8
- package/dist/next/server/index.js +26 -18
- package/dist/react/index.cjs +650 -0
- package/dist/react/index.d.cts +213 -0
- package/dist/react/index.d.ts +213 -0
- package/dist/react/index.js +622 -0
- package/dist/server/index.cjs +703 -0
- package/dist/server/index.d.cts +379 -0
- package/dist/server/index.d.ts +379 -0
- package/dist/server/index.js +687 -0
- package/package.json +17 -7
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React6 = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var logry = require('logry');
|
|
6
|
+
var Keyv = require('keyv');
|
|
7
|
+
var merge = require('lodash.merge');
|
|
8
|
+
var intorTranslator = require('intor-translator');
|
|
9
|
+
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
function _interopNamespace(e) {
|
|
13
|
+
if (e && e.__esModule) return e;
|
|
14
|
+
var n = Object.create(null);
|
|
15
|
+
if (e) {
|
|
16
|
+
Object.keys(e).forEach(function (k) {
|
|
17
|
+
if (k !== 'default') {
|
|
18
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return e[k]; }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
n.default = e;
|
|
27
|
+
return Object.freeze(n);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var React6__namespace = /*#__PURE__*/_interopNamespace(React6);
|
|
31
|
+
var Keyv__default = /*#__PURE__*/_interopDefault(Keyv);
|
|
32
|
+
var merge__default = /*#__PURE__*/_interopDefault(merge);
|
|
33
|
+
|
|
34
|
+
// src/client/react/contexts/intor-provider/intor-provider.tsx
|
|
35
|
+
var ConfigContext = React6__namespace.createContext(void 0);
|
|
36
|
+
function ConfigProvider({
|
|
37
|
+
value: { config, pathname },
|
|
38
|
+
children
|
|
39
|
+
}) {
|
|
40
|
+
const value = React6__namespace.useMemo(() => ({ config, pathname }), [config, pathname]);
|
|
41
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ConfigContext.Provider, { value, children });
|
|
42
|
+
}
|
|
43
|
+
function useConfig() {
|
|
44
|
+
const context = React6__namespace.useContext(ConfigContext);
|
|
45
|
+
if (!context) throw new Error("useConfig must be used within ConfigProvider");
|
|
46
|
+
return context;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/config/constants/cache.constants.ts
|
|
50
|
+
var DEFAULT_CACHE_OPTIONS = {
|
|
51
|
+
enabled: process.env.NODE_ENV === "production",
|
|
52
|
+
ttl: 60 * 60 * 1e3
|
|
53
|
+
// 1 hour
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/server/shared/logger/global-logger-pool.ts
|
|
57
|
+
function getGlobalLoggerPool() {
|
|
58
|
+
if (!globalThis.__INTOR_LOGGER_POOL__) {
|
|
59
|
+
globalThis.__INTOR_LOGGER_POOL__ = /* @__PURE__ */ new Map();
|
|
60
|
+
}
|
|
61
|
+
return globalThis.__INTOR_LOGGER_POOL__;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/server/shared/logger/get-logger.ts
|
|
65
|
+
var DEFAULT_FORMATTER_CONFIG = {
|
|
66
|
+
node: { meta: { compact: true }, lineBreaksAfter: 1 }
|
|
67
|
+
};
|
|
68
|
+
function getLogger({
|
|
69
|
+
id = "default",
|
|
70
|
+
formatterConfig,
|
|
71
|
+
preset,
|
|
72
|
+
...options
|
|
73
|
+
}) {
|
|
74
|
+
const pool = getGlobalLoggerPool();
|
|
75
|
+
let logger = pool.get(id);
|
|
76
|
+
const useDefault = !formatterConfig && !preset;
|
|
77
|
+
if (!logger) {
|
|
78
|
+
logger = logry.logry({
|
|
79
|
+
id,
|
|
80
|
+
formatterConfig: useDefault ? DEFAULT_FORMATTER_CONFIG : formatterConfig,
|
|
81
|
+
preset,
|
|
82
|
+
...options
|
|
83
|
+
});
|
|
84
|
+
pool.set(id, logger);
|
|
85
|
+
if (pool.size > 1e3) {
|
|
86
|
+
const keys = [...pool.keys()];
|
|
87
|
+
for (const key of keys.slice(0, 200)) pool.delete(key);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return logger;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/server/messages/shared/utils/is-valid-messages.ts
|
|
94
|
+
function isPlainObject(value) {
|
|
95
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
96
|
+
}
|
|
97
|
+
function isValidMessages(value) {
|
|
98
|
+
if (!isPlainObject(value)) return false;
|
|
99
|
+
const stack = [value];
|
|
100
|
+
while (stack.length > 0) {
|
|
101
|
+
const current = stack.pop();
|
|
102
|
+
for (const v of Object.values(current)) {
|
|
103
|
+
if (typeof v === "string") continue;
|
|
104
|
+
if (isPlainObject(v)) {
|
|
105
|
+
stack.push(v);
|
|
106
|
+
} else {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
function getGlobalMessagesPool() {
|
|
114
|
+
if (!globalThis.__INTOR_MESSAGES_POOL__) {
|
|
115
|
+
globalThis.__INTOR_MESSAGES_POOL__ = new Keyv__default.default();
|
|
116
|
+
}
|
|
117
|
+
return globalThis.__INTOR_MESSAGES_POOL__;
|
|
118
|
+
}
|
|
119
|
+
var mergeMessages = (staticMessages = {}, loadedMessages = {}) => {
|
|
120
|
+
if (!loadedMessages) return { ...staticMessages };
|
|
121
|
+
return merge__default.default({}, staticMessages, loadedMessages);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// src/shared/utils/normalize-cache-key.ts
|
|
125
|
+
var CACHE_KEY_DELIMITER = "|";
|
|
126
|
+
var sanitize = (k) => k.replaceAll(/[\u200B-\u200D\uFEFF]/g, "").replaceAll(/[\r\n]/g, "").trim();
|
|
127
|
+
var normalizeCacheKey = (key, delimiter = CACHE_KEY_DELIMITER) => {
|
|
128
|
+
if (key === null || key === void 0) return null;
|
|
129
|
+
if (Array.isArray(key)) {
|
|
130
|
+
if (key.length === 0) return null;
|
|
131
|
+
const normalized = key.map((k) => {
|
|
132
|
+
if (k === null) return "__null";
|
|
133
|
+
if (k === void 0) return "__undefined";
|
|
134
|
+
if (typeof k === "boolean") return k ? "__true" : "__false";
|
|
135
|
+
return sanitize(String(k));
|
|
136
|
+
});
|
|
137
|
+
return normalized.join(delimiter);
|
|
138
|
+
}
|
|
139
|
+
if (typeof key === "boolean") return key ? "__true" : "__false";
|
|
140
|
+
return String(key);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/shared/constants/prefix-placeholder.ts
|
|
144
|
+
var PREFIX_PLACEHOLDER = "{locale}";
|
|
145
|
+
|
|
146
|
+
// src/shared/utils/resolve-namespaces.ts
|
|
147
|
+
var resolveNamespaces = ({
|
|
148
|
+
config,
|
|
149
|
+
pathname
|
|
150
|
+
}) => {
|
|
151
|
+
const { loader } = config;
|
|
152
|
+
const { routeNamespaces = {}, namespaces } = loader || {};
|
|
153
|
+
if (Object.keys(routeNamespaces).length === 0 && !namespaces)
|
|
154
|
+
return void 0;
|
|
155
|
+
const standardizedPathname = standardizePathname({ config, pathname });
|
|
156
|
+
const placeholderRemovedPathname = standardizedPathname.replace(
|
|
157
|
+
`/${PREFIX_PLACEHOLDER}`,
|
|
158
|
+
""
|
|
159
|
+
);
|
|
160
|
+
const collected = [
|
|
161
|
+
...routeNamespaces.default || [],
|
|
162
|
+
// default
|
|
163
|
+
...namespaces || [],
|
|
164
|
+
// default
|
|
165
|
+
...routeNamespaces[standardizedPathname] || [],
|
|
166
|
+
// exact match
|
|
167
|
+
...routeNamespaces[placeholderRemovedPathname] || []
|
|
168
|
+
// exact match
|
|
169
|
+
];
|
|
170
|
+
const prefixPatterns = Object.keys(routeNamespaces).filter(
|
|
171
|
+
(pattern) => pattern.endsWith("/*")
|
|
172
|
+
);
|
|
173
|
+
for (const pattern of prefixPatterns) {
|
|
174
|
+
const basePath = pattern.replace(/\/\*$/, "");
|
|
175
|
+
if (standardizedPathname.startsWith(basePath) || placeholderRemovedPathname.startsWith(basePath)) {
|
|
176
|
+
collected.push(...routeNamespaces[pattern] || []);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return [...new Set(collected)];
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/shared/utils/pathname/normalize-pathname.ts
|
|
183
|
+
var normalizePathname = (rawPathname, options = {}) => {
|
|
184
|
+
const length = rawPathname.length;
|
|
185
|
+
let start = 0;
|
|
186
|
+
let end = length - 1;
|
|
187
|
+
while (start <= end && (rawPathname.codePointAt(start) ?? 0) <= 32) start++;
|
|
188
|
+
while (end >= start && (rawPathname.codePointAt(end) ?? 0) <= 32) end--;
|
|
189
|
+
if (start > end) return "/";
|
|
190
|
+
let result = "";
|
|
191
|
+
let hasSlash = false;
|
|
192
|
+
for (let i = start; i <= end; i++) {
|
|
193
|
+
const char = rawPathname[i];
|
|
194
|
+
if (char === "/") {
|
|
195
|
+
if (!hasSlash) {
|
|
196
|
+
hasSlash = true;
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
result += hasSlash || result === "" ? "/" + char : char;
|
|
200
|
+
hasSlash = false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (options.removeLeadingSlash && result.startsWith("/")) {
|
|
204
|
+
result = result.slice(1);
|
|
205
|
+
}
|
|
206
|
+
return result || "/";
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// src/shared/utils/pathname/standardize-pathname.ts
|
|
210
|
+
var standardizePathname = ({
|
|
211
|
+
config,
|
|
212
|
+
pathname
|
|
213
|
+
}) => {
|
|
214
|
+
const { routing } = config;
|
|
215
|
+
const { basePath } = routing;
|
|
216
|
+
const parts = [
|
|
217
|
+
normalizePathname(basePath),
|
|
218
|
+
PREFIX_PLACEHOLDER,
|
|
219
|
+
normalizePathname(pathname)
|
|
220
|
+
];
|
|
221
|
+
const standardizedPathname = parts.join("/").replaceAll(/\/{2,}/g, "/");
|
|
222
|
+
return normalizePathname(standardizedPathname);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// src/server/messages/load-remote-messages/fetch-locale-messages/fetch-locale-messages.ts
|
|
226
|
+
var fetchLocaleMessages = async ({
|
|
227
|
+
remoteUrl,
|
|
228
|
+
remoteHeaders,
|
|
229
|
+
searchParams,
|
|
230
|
+
locale,
|
|
231
|
+
extraOptions: { loggerOptions } = {}
|
|
232
|
+
}) => {
|
|
233
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
234
|
+
const logger = baseLogger.child({ scope: "fetch-locale-messages" });
|
|
235
|
+
try {
|
|
236
|
+
const params = new URLSearchParams(searchParams);
|
|
237
|
+
params.append("locale", locale);
|
|
238
|
+
const url = `${remoteUrl}?${params.toString()}`;
|
|
239
|
+
const headers = {
|
|
240
|
+
"Content-Type": "application/json",
|
|
241
|
+
...remoteHeaders
|
|
242
|
+
};
|
|
243
|
+
const response = await fetch(url, {
|
|
244
|
+
method: "GET",
|
|
245
|
+
headers,
|
|
246
|
+
cache: "no-store"
|
|
247
|
+
});
|
|
248
|
+
if (!response.ok) {
|
|
249
|
+
throw new Error(`HTTP error ${response.status} ${response.statusText}`);
|
|
250
|
+
}
|
|
251
|
+
const data = await response.json();
|
|
252
|
+
if (!isValidMessages(data[locale])) {
|
|
253
|
+
throw new Error("JSON file does not match NamespaceMessages structure");
|
|
254
|
+
}
|
|
255
|
+
return data;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
logger.warn("Fetching locale messages failed.", {
|
|
258
|
+
locale,
|
|
259
|
+
remoteUrl,
|
|
260
|
+
searchParams: decodeURIComponent(searchParams.toString()),
|
|
261
|
+
error
|
|
262
|
+
});
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// src/server/messages/load-remote-messages/fetch-locale-messages/utils/build-search-params.ts
|
|
268
|
+
var buildSearchParams = (params) => {
|
|
269
|
+
const searchParams = new URLSearchParams();
|
|
270
|
+
const appendParam = (key, value) => {
|
|
271
|
+
if (value === void 0 || value === null) return;
|
|
272
|
+
if (Array.isArray(value) && value.length === 0) return;
|
|
273
|
+
if (Array.isArray(value)) {
|
|
274
|
+
value.forEach((v) => v && searchParams.append(key, v));
|
|
275
|
+
} else {
|
|
276
|
+
searchParams.append(key, value);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
280
|
+
appendParam(key, value);
|
|
281
|
+
});
|
|
282
|
+
return searchParams;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// src/server/messages/load-remote-messages/load-remote-messages.ts
|
|
286
|
+
var loadRemoteMessages = async ({
|
|
287
|
+
pool = getGlobalMessagesPool(),
|
|
288
|
+
rootDir,
|
|
289
|
+
remoteUrl,
|
|
290
|
+
remoteHeaders,
|
|
291
|
+
locale,
|
|
292
|
+
fallbackLocales = [],
|
|
293
|
+
namespaces = [],
|
|
294
|
+
extraOptions: {
|
|
295
|
+
cacheOptions = DEFAULT_CACHE_OPTIONS,
|
|
296
|
+
loggerOptions = { id: "default" }
|
|
297
|
+
} = {},
|
|
298
|
+
allowCacheWrite
|
|
299
|
+
}) => {
|
|
300
|
+
const baseLogger = getLogger({ ...loggerOptions });
|
|
301
|
+
const logger = baseLogger.child({ scope: "load-remote-messages" });
|
|
302
|
+
const start = performance.now();
|
|
303
|
+
logger.debug("Loading remote messages from api.", { remoteUrl });
|
|
304
|
+
const key = normalizeCacheKey([
|
|
305
|
+
loggerOptions.id,
|
|
306
|
+
"loaderType:remote",
|
|
307
|
+
rootDir,
|
|
308
|
+
locale,
|
|
309
|
+
(fallbackLocales ?? []).toSorted().join(","),
|
|
310
|
+
(namespaces ?? []).toSorted().join(",")
|
|
311
|
+
]);
|
|
312
|
+
if (cacheOptions.enabled && key) {
|
|
313
|
+
const cached = await pool?.get(key);
|
|
314
|
+
if (cached) {
|
|
315
|
+
logger.debug("Messages cache hit.", { key });
|
|
316
|
+
return cached;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const searchParams = buildSearchParams({ rootDir, namespaces });
|
|
320
|
+
const candidateLocales = [locale, ...fallbackLocales || []];
|
|
321
|
+
let messages;
|
|
322
|
+
for (const candidateLocale of candidateLocales) {
|
|
323
|
+
try {
|
|
324
|
+
const result = await fetchLocaleMessages({
|
|
325
|
+
remoteUrl,
|
|
326
|
+
remoteHeaders,
|
|
327
|
+
searchParams,
|
|
328
|
+
locale: candidateLocale,
|
|
329
|
+
extraOptions: { loggerOptions }
|
|
330
|
+
});
|
|
331
|
+
if (result && Object.values(result[candidateLocale] || {}).length > 0) {
|
|
332
|
+
messages = result;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
logger.error("Failed to fetch locale messages.", {
|
|
337
|
+
locale: candidateLocale,
|
|
338
|
+
error
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (allowCacheWrite && cacheOptions.enabled && key && messages) {
|
|
343
|
+
await pool?.set(key, messages, cacheOptions.ttl);
|
|
344
|
+
}
|
|
345
|
+
const end = performance.now();
|
|
346
|
+
const duration = Math.round(end - start);
|
|
347
|
+
logger.trace("Finished loading remote messages.", {
|
|
348
|
+
loadedLocale: messages ? Object.keys(messages)[0] : void 0,
|
|
349
|
+
duration: `${duration} ms`
|
|
350
|
+
});
|
|
351
|
+
return messages;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/client/react/contexts/messages/utils/use-refetch-messages.ts
|
|
355
|
+
var useRefetchMessages = ({
|
|
356
|
+
config,
|
|
357
|
+
pathname,
|
|
358
|
+
setLoadedMessages,
|
|
359
|
+
setIsLoadingMessages
|
|
360
|
+
}) => {
|
|
361
|
+
const { messages: staticMessages } = config;
|
|
362
|
+
const namespaces = React6__namespace.useMemo(() => {
|
|
363
|
+
if (!config.loader) return [];
|
|
364
|
+
return resolveNamespaces({ config, pathname });
|
|
365
|
+
}, [config, pathname]);
|
|
366
|
+
const refetchMessages = React6__namespace.useCallback(
|
|
367
|
+
async (newLocale) => {
|
|
368
|
+
if (config.loader?.type === "remote") {
|
|
369
|
+
setIsLoadingMessages(true);
|
|
370
|
+
const loadedMessages = await loadRemoteMessages({
|
|
371
|
+
rootDir: config.loader.rootDir,
|
|
372
|
+
remoteUrl: config.loader.remoteUrl,
|
|
373
|
+
remoteHeaders: config.loader.remoteHeaders,
|
|
374
|
+
locale: newLocale,
|
|
375
|
+
fallbackLocales: config.fallbackLocales[newLocale] || [],
|
|
376
|
+
namespaces,
|
|
377
|
+
extraOptions: {
|
|
378
|
+
cacheOptions: config.cache,
|
|
379
|
+
loggerOptions: { id: config.id }
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
const messages = mergeMessages(staticMessages, loadedMessages);
|
|
383
|
+
setLoadedMessages(messages);
|
|
384
|
+
setIsLoadingMessages(false);
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
[
|
|
388
|
+
config.loader,
|
|
389
|
+
config.fallbackLocales,
|
|
390
|
+
config.id,
|
|
391
|
+
setIsLoadingMessages,
|
|
392
|
+
namespaces,
|
|
393
|
+
staticMessages,
|
|
394
|
+
setLoadedMessages
|
|
395
|
+
]
|
|
396
|
+
);
|
|
397
|
+
return { refetchMessages };
|
|
398
|
+
};
|
|
399
|
+
var MessagesContext = React6__namespace.createContext(void 0);
|
|
400
|
+
function MessagesProvider({
|
|
401
|
+
value: { messages = {} },
|
|
402
|
+
children
|
|
403
|
+
}) {
|
|
404
|
+
const { config, pathname } = useConfig();
|
|
405
|
+
const [loadedMessages, setLoadedMessages] = React6__namespace.useState(null);
|
|
406
|
+
const [isLoadingMessages, setIsLoadingMessages] = React6__namespace.useState(false);
|
|
407
|
+
const { refetchMessages } = useRefetchMessages({
|
|
408
|
+
config,
|
|
409
|
+
pathname,
|
|
410
|
+
setLoadedMessages,
|
|
411
|
+
setIsLoadingMessages
|
|
412
|
+
});
|
|
413
|
+
const value = React6__namespace.useMemo(
|
|
414
|
+
() => ({
|
|
415
|
+
messages: loadedMessages || messages,
|
|
416
|
+
isLoading: isLoadingMessages,
|
|
417
|
+
setLoadedMessages,
|
|
418
|
+
setIsLoadingMessages,
|
|
419
|
+
refetchMessages
|
|
420
|
+
}),
|
|
421
|
+
[loadedMessages, messages, isLoadingMessages, refetchMessages]
|
|
422
|
+
);
|
|
423
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MessagesContext.Provider, { value, children });
|
|
424
|
+
}
|
|
425
|
+
function useMessages() {
|
|
426
|
+
const context = React6__namespace.useContext(MessagesContext);
|
|
427
|
+
if (!context)
|
|
428
|
+
throw new Error("useMessages must be used within a MessagesProvider");
|
|
429
|
+
return context;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/client/react/contexts/locale/utils/use-init-lazy-load.ts
|
|
433
|
+
var useInitLazyLoad = ({
|
|
434
|
+
loaderOptions,
|
|
435
|
+
currentLocale
|
|
436
|
+
}) => {
|
|
437
|
+
const { refetchMessages } = useMessages();
|
|
438
|
+
const lazyLoad = !!loaderOptions?.lazyLoad;
|
|
439
|
+
const isFirstLoadedRef = React6__namespace.useRef(false);
|
|
440
|
+
React6__namespace.useEffect(() => {
|
|
441
|
+
if (lazyLoad && !isFirstLoadedRef.current) {
|
|
442
|
+
void refetchMessages(currentLocale);
|
|
443
|
+
isFirstLoadedRef.current = true;
|
|
444
|
+
}
|
|
445
|
+
}, [lazyLoad, currentLocale, refetchMessages, isFirstLoadedRef]);
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// src/shared/utils/client/build-cookie-string.ts
|
|
449
|
+
var buildCookieString = (cookie, locale) => {
|
|
450
|
+
const parts = [`${cookie.name}=${encodeURIComponent(locale)}`];
|
|
451
|
+
if (cookie.maxAge) {
|
|
452
|
+
const expires = new Date(Date.now() + cookie.maxAge * 1e3).toUTCString();
|
|
453
|
+
parts.push(`expires=${expires}`, `max-age=${cookie.maxAge}`);
|
|
454
|
+
}
|
|
455
|
+
parts.push(`path=${cookie.path ?? "/"}`);
|
|
456
|
+
if (cookie.domain) {
|
|
457
|
+
parts.push(`domain=${cookie.domain}`);
|
|
458
|
+
}
|
|
459
|
+
if (cookie.sameSite) {
|
|
460
|
+
parts.push(
|
|
461
|
+
`SameSite=${cookie.sameSite[0].toUpperCase()}${cookie.sameSite.slice(1).toLowerCase()}`
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
if (cookie.secure !== false) {
|
|
465
|
+
parts.push(`Secure`);
|
|
466
|
+
}
|
|
467
|
+
return parts.join("; ");
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// src/shared/utils/client/set-locale-cookie-browser.ts
|
|
471
|
+
var setLocaleCookieBrowser = ({
|
|
472
|
+
cookie,
|
|
473
|
+
locale
|
|
474
|
+
}) => {
|
|
475
|
+
if (globalThis.window === void 0) return;
|
|
476
|
+
if (cookie.disabled || !cookie.autoSetCookie) return;
|
|
477
|
+
const cookieString = buildCookieString(cookie, locale);
|
|
478
|
+
document.cookie = cookieString;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/client/react/contexts/locale/utils/use-init-locale-cookie.ts
|
|
482
|
+
var useInitLocaleCookie = ({
|
|
483
|
+
config,
|
|
484
|
+
locale
|
|
485
|
+
}) => {
|
|
486
|
+
React6__namespace.useEffect(() => {
|
|
487
|
+
if (typeof document === "undefined") return;
|
|
488
|
+
const { cookie, routing } = config;
|
|
489
|
+
const { firstVisit } = routing;
|
|
490
|
+
const cookies = document.cookie.split(";").map((c) => c.trim());
|
|
491
|
+
const isCookieExists = cookies.some((c) => c.startsWith(`${cookie.name}=`));
|
|
492
|
+
if (isCookieExists) return;
|
|
493
|
+
if (!firstVisit.redirect) return;
|
|
494
|
+
if (cookie.disabled || !cookie.autoSetCookie) return;
|
|
495
|
+
setLocaleCookieBrowser({ cookie, locale });
|
|
496
|
+
}, []);
|
|
497
|
+
};
|
|
498
|
+
var LocaleContext = React6__namespace.createContext(void 0);
|
|
499
|
+
|
|
500
|
+
// src/client/react/contexts/locale/utils/change-locale.ts
|
|
501
|
+
var changeLocale = ({
|
|
502
|
+
currentLocale,
|
|
503
|
+
newLocale,
|
|
504
|
+
loaderOptions,
|
|
505
|
+
cookie,
|
|
506
|
+
setLocale,
|
|
507
|
+
refetchMessages
|
|
508
|
+
}) => {
|
|
509
|
+
if (typeof document === "undefined") return;
|
|
510
|
+
const loaderType = loaderOptions?.type;
|
|
511
|
+
if (newLocale === currentLocale) return;
|
|
512
|
+
if (loaderType === "local") {
|
|
513
|
+
console.warn(
|
|
514
|
+
`[Intor] You are using dynamic local to switch languages. Please make sure to use the wrapped <Link> component to trigger a page reload, ensuring that the translation data is dynamically updated.`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
setLocale(newLocale);
|
|
518
|
+
setLocaleCookieBrowser({ cookie, locale: newLocale });
|
|
519
|
+
document.documentElement.lang = newLocale;
|
|
520
|
+
if (loaderType === "remote" && refetchMessages) {
|
|
521
|
+
void refetchMessages(newLocale);
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
function LocaleProvider({
|
|
525
|
+
value: { initialLocale },
|
|
526
|
+
children
|
|
527
|
+
}) {
|
|
528
|
+
const { config } = useConfig();
|
|
529
|
+
const { refetchMessages } = useMessages();
|
|
530
|
+
const { loader: loaderOptions, cookie } = config;
|
|
531
|
+
const [currentLocale, setCurrentLocale] = React6__namespace.useState(initialLocale);
|
|
532
|
+
useInitLazyLoad({ loaderOptions, currentLocale });
|
|
533
|
+
useInitLocaleCookie({ config, locale: initialLocale });
|
|
534
|
+
const setLocale = React6__namespace.useCallback(
|
|
535
|
+
(newLocale) => {
|
|
536
|
+
changeLocale({
|
|
537
|
+
currentLocale,
|
|
538
|
+
newLocale,
|
|
539
|
+
loaderOptions,
|
|
540
|
+
cookie,
|
|
541
|
+
setLocale: setCurrentLocale,
|
|
542
|
+
refetchMessages
|
|
543
|
+
});
|
|
544
|
+
},
|
|
545
|
+
[currentLocale, loaderOptions, cookie, refetchMessages]
|
|
546
|
+
);
|
|
547
|
+
const value = React6__namespace.useMemo(
|
|
548
|
+
() => ({
|
|
549
|
+
locale: currentLocale,
|
|
550
|
+
setLocale
|
|
551
|
+
}),
|
|
552
|
+
[currentLocale, setLocale]
|
|
553
|
+
);
|
|
554
|
+
return /* @__PURE__ */ jsxRuntime.jsx(LocaleContext.Provider, { value, children });
|
|
555
|
+
}
|
|
556
|
+
function useLocale() {
|
|
557
|
+
const context = React6__namespace.useContext(LocaleContext);
|
|
558
|
+
if (!context)
|
|
559
|
+
throw new Error("useLocale must be used within a LocaleProvider");
|
|
560
|
+
return context;
|
|
561
|
+
}
|
|
562
|
+
var TranslateHandlersContext = React6__namespace.createContext(void 0);
|
|
563
|
+
var TranslateHandlersProvider = ({
|
|
564
|
+
children,
|
|
565
|
+
handlers
|
|
566
|
+
}) => {
|
|
567
|
+
const value = handlers;
|
|
568
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TranslateHandlersContext.Provider, { value, children });
|
|
569
|
+
};
|
|
570
|
+
function useTranslateHandlers() {
|
|
571
|
+
const context = React6__namespace.useContext(TranslateHandlersContext);
|
|
572
|
+
return context;
|
|
573
|
+
}
|
|
574
|
+
var useInitLoadingState = (config) => {
|
|
575
|
+
const lazyLoad = !!config.loader?.lazyLoad;
|
|
576
|
+
const [isCsr, setIsCsr] = React6__namespace.useState(false);
|
|
577
|
+
React6__namespace.useEffect(() => {
|
|
578
|
+
setIsCsr(true);
|
|
579
|
+
}, []);
|
|
580
|
+
const isBeforeCSRLoading = lazyLoad && !isCsr;
|
|
581
|
+
return isBeforeCSRLoading;
|
|
582
|
+
};
|
|
583
|
+
var TranslatorContext = React6__namespace.createContext(void 0);
|
|
584
|
+
var EMPTY_OBJECT = Object.freeze({});
|
|
585
|
+
function TranslatorProvider({ children }) {
|
|
586
|
+
const { config } = useConfig();
|
|
587
|
+
const { messages, isLoading } = useMessages();
|
|
588
|
+
const { locale } = useLocale();
|
|
589
|
+
const translatorHandlers = useTranslateHandlers();
|
|
590
|
+
const { fallbackLocales, translator: translatorOptions } = config;
|
|
591
|
+
const isBeforeCSRLoading = useInitLoadingState(config);
|
|
592
|
+
const value = React6__namespace.useMemo(() => {
|
|
593
|
+
const translator = new intorTranslator.Translator({
|
|
594
|
+
messages: messages || EMPTY_OBJECT,
|
|
595
|
+
locale,
|
|
596
|
+
fallbackLocales,
|
|
597
|
+
loadingMessage: translatorOptions?.loadingMessage,
|
|
598
|
+
placeholder: translatorOptions?.placeholder,
|
|
599
|
+
handlers: translatorHandlers
|
|
600
|
+
});
|
|
601
|
+
translator.setLoading(isBeforeCSRLoading || isLoading);
|
|
602
|
+
return { translator };
|
|
603
|
+
}, [
|
|
604
|
+
fallbackLocales,
|
|
605
|
+
isBeforeCSRLoading,
|
|
606
|
+
isLoading,
|
|
607
|
+
locale,
|
|
608
|
+
messages,
|
|
609
|
+
translatorHandlers,
|
|
610
|
+
translatorOptions?.loadingMessage,
|
|
611
|
+
translatorOptions?.placeholder
|
|
612
|
+
]);
|
|
613
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TranslatorContext.Provider, { value, children });
|
|
614
|
+
}
|
|
615
|
+
function useTranslator() {
|
|
616
|
+
const context = React6__namespace.useContext(TranslatorContext);
|
|
617
|
+
if (!context)
|
|
618
|
+
throw new Error(
|
|
619
|
+
"useTranslator must be used within IntorTranslatorProvider"
|
|
620
|
+
);
|
|
621
|
+
return context;
|
|
622
|
+
}
|
|
623
|
+
var IntorProvider = ({
|
|
624
|
+
value: { config, pathname = "", initialLocale, messages = config.messages },
|
|
625
|
+
children
|
|
626
|
+
}) => {
|
|
627
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ConfigProvider, { value: { config, pathname }, children: /* @__PURE__ */ jsxRuntime.jsx(MessagesProvider, { value: { messages }, children: /* @__PURE__ */ jsxRuntime.jsx(LocaleProvider, { value: { initialLocale }, children: /* @__PURE__ */ jsxRuntime.jsx(TranslatorProvider, { children }) }) }) });
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// src/client/react/hooks/use-translator.ts
|
|
631
|
+
function useTranslator2(preKey) {
|
|
632
|
+
const { translator } = useTranslator();
|
|
633
|
+
const { setLocale } = useLocale();
|
|
634
|
+
const props = {
|
|
635
|
+
messages: translator.messages,
|
|
636
|
+
locale: translator.locale,
|
|
637
|
+
isLoading: translator.isLoading,
|
|
638
|
+
setLocale
|
|
639
|
+
};
|
|
640
|
+
const scoped = translator.scoped(preKey);
|
|
641
|
+
return {
|
|
642
|
+
...props,
|
|
643
|
+
hasKey: preKey ? scoped.hasKey : translator.hasKey,
|
|
644
|
+
t: preKey ? scoped.t : translator.t
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
exports.IntorProvider = IntorProvider;
|
|
649
|
+
exports.TranslateHandlersProvider = TranslateHandlersProvider;
|
|
650
|
+
exports.useTranslator = useTranslator2;
|