intor 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.cjs +4 -8
- package/dist/config/index.d.cts +3 -3
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.js +4 -8
- package/dist/index.cjs +88 -120
- package/dist/index.d.cts +65 -39
- package/dist/index.d.ts +65 -39
- package/dist/index.js +88 -120
- package/dist/next/index.cjs +112 -131
- package/dist/next/index.d.cts +54 -27
- package/dist/next/index.d.ts +54 -27
- package/dist/next/index.js +112 -131
- package/dist/next/middleware/index.cjs +24 -36
- package/dist/next/middleware/index.d.cts +3 -3
- package/dist/next/middleware/index.d.ts +3 -3
- package/dist/next/middleware/index.js +24 -36
- package/dist/next/server/index.cjs +69 -132
- package/dist/next/server/index.d.cts +52 -25
- package/dist/next/server/index.d.ts +52 -25
- package/dist/next/server/index.js +69 -132
- package/package.json +13 -9
|
@@ -50,7 +50,7 @@ function getLogger({
|
|
|
50
50
|
});
|
|
51
51
|
pool.set(id, logger);
|
|
52
52
|
if (pool.size > 1e3) {
|
|
53
|
-
const keys =
|
|
53
|
+
const keys = [...pool.keys()];
|
|
54
54
|
for (const key of keys.slice(0, 200)) pool.delete(key);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -61,7 +61,7 @@ function getLogger({
|
|
|
61
61
|
var CACHE_KEY_DELIMITER = "|";
|
|
62
62
|
var sanitize = (k) => k.replaceAll(/[\u200B-\u200D\uFEFF]/g, "").replaceAll(/[\r\n]/g, "").trim();
|
|
63
63
|
var normalizeCacheKey = (key, delimiter = CACHE_KEY_DELIMITER) => {
|
|
64
|
-
if (
|
|
64
|
+
if (key === null || key === void 0) return null;
|
|
65
65
|
if (Array.isArray(key)) {
|
|
66
66
|
if (key.length === 0) return null;
|
|
67
67
|
const normalized = key.map((k) => {
|
|
@@ -85,56 +85,44 @@ var resolveNamespaces = ({
|
|
|
85
85
|
pathname
|
|
86
86
|
}) => {
|
|
87
87
|
const { loader } = config;
|
|
88
|
-
const {
|
|
89
|
-
|
|
90
|
-
namespaces: fallbackNamespaces
|
|
91
|
-
} = loader;
|
|
92
|
-
const { unprefixedPathname } = extractPathname({ config, pathname });
|
|
93
|
-
const standardizedPathname = standardizePathname({
|
|
94
|
-
config,
|
|
95
|
-
pathname: unprefixedPathname
|
|
96
|
-
});
|
|
88
|
+
const { routeNamespaces = {}, namespaces } = loader || {};
|
|
89
|
+
const standardizedPathname = standardizePathname({ config, pathname });
|
|
97
90
|
const placeholderRemovedPathname = standardizedPathname.replace(
|
|
98
91
|
`/${PREFIX_PLACEHOLDER}`,
|
|
99
92
|
""
|
|
100
93
|
);
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
94
|
+
const collected = [
|
|
95
|
+
...routeNamespaces.default || [],
|
|
96
|
+
// default
|
|
97
|
+
...namespaces || [],
|
|
98
|
+
// default
|
|
99
|
+
...routeNamespaces[standardizedPathname] || [],
|
|
100
|
+
// exact match
|
|
101
|
+
...routeNamespaces[placeholderRemovedPathname] || []
|
|
102
|
+
// exact match
|
|
103
|
+
];
|
|
108
104
|
const prefixPatterns = Object.keys(routeNamespaces).filter(
|
|
109
105
|
(pattern) => pattern.endsWith("/*")
|
|
110
106
|
);
|
|
111
107
|
for (const pattern of prefixPatterns) {
|
|
112
108
|
const basePath = pattern.replace(/\/\*$/, "");
|
|
113
|
-
if (standardizedPathname.startsWith(basePath)) {
|
|
114
|
-
|
|
115
|
-
bestMatch = basePath;
|
|
116
|
-
bestNamespaces = routeNamespaces[pattern];
|
|
117
|
-
}
|
|
109
|
+
if (standardizedPathname.startsWith(basePath) || placeholderRemovedPathname.startsWith(basePath)) {
|
|
110
|
+
collected.push(...routeNamespaces[pattern] || []);
|
|
118
111
|
}
|
|
119
112
|
}
|
|
120
|
-
|
|
121
|
-
if (matchedNamespaces.length > 0) {
|
|
122
|
-
return [...defaultNamespaces, ...matchedNamespaces];
|
|
123
|
-
} else {
|
|
124
|
-
return [...defaultNamespaces];
|
|
125
|
-
}
|
|
113
|
+
return [...new Set(collected)];
|
|
126
114
|
};
|
|
127
115
|
|
|
128
116
|
// src/shared/utils/locale/normalize-locale.ts
|
|
117
|
+
var toCanonical = (input) => {
|
|
118
|
+
try {
|
|
119
|
+
return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
|
|
120
|
+
} catch {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
129
124
|
var normalizeLocale = (locale = "", supportedLocales = []) => {
|
|
130
125
|
if (!locale || supportedLocales.length === 0) return;
|
|
131
|
-
const toCanonical = (input) => {
|
|
132
|
-
try {
|
|
133
|
-
return Intl.getCanonicalLocales(input)[0]?.toLowerCase();
|
|
134
|
-
} catch {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
126
|
const canonicalLocale = toCanonical(locale);
|
|
139
127
|
if (!canonicalLocale) return;
|
|
140
128
|
const supportedCanonicalMap = /* @__PURE__ */ new Map();
|
|
@@ -165,12 +153,12 @@ var resolvePreferredLocale = (acceptLanguageHeader, supportedLocales) => {
|
|
|
165
153
|
const supportedLocalesSet = new Set(supportedLocales);
|
|
166
154
|
const preferred = acceptLanguageHeader.split(",").map((part) => {
|
|
167
155
|
const [lang, qValue] = part.split(";");
|
|
168
|
-
const q = qValue ? parseFloat(qValue.split("=")[1]) : 1;
|
|
169
|
-
if (isNaN(q)) {
|
|
156
|
+
const q = qValue ? Number.parseFloat(qValue.split("=")[1]) : 1;
|
|
157
|
+
if (Number.isNaN(q)) {
|
|
170
158
|
return { lang: lang.trim(), q: 0 };
|
|
171
159
|
}
|
|
172
160
|
return { lang: lang.trim(), q };
|
|
173
|
-
}).
|
|
161
|
+
}).toSorted((a, b) => b.q - a.q).find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
|
|
174
162
|
return preferred;
|
|
175
163
|
};
|
|
176
164
|
|
|
@@ -179,8 +167,8 @@ var normalizePathname = (rawPathname, options = {}) => {
|
|
|
179
167
|
const length = rawPathname.length;
|
|
180
168
|
let start = 0;
|
|
181
169
|
let end = length - 1;
|
|
182
|
-
while (start <= end && rawPathname.
|
|
183
|
-
while (end >= start && rawPathname.
|
|
170
|
+
while (start <= end && (rawPathname.codePointAt(start) ?? 0) <= 32) start++;
|
|
171
|
+
while (end >= start && (rawPathname.codePointAt(end) ?? 0) <= 32) end--;
|
|
184
172
|
if (start > end) return "/";
|
|
185
173
|
let result = "";
|
|
186
174
|
let hasSlash = false;
|
|
@@ -191,11 +179,7 @@ var normalizePathname = (rawPathname, options = {}) => {
|
|
|
191
179
|
hasSlash = true;
|
|
192
180
|
}
|
|
193
181
|
} else {
|
|
194
|
-
|
|
195
|
-
result += "/" + char;
|
|
196
|
-
} else {
|
|
197
|
-
result += char;
|
|
198
|
-
}
|
|
182
|
+
result += hasSlash || result === "" ? "/" + char : char;
|
|
199
183
|
hasSlash = false;
|
|
200
184
|
}
|
|
201
185
|
}
|
|
@@ -205,42 +189,6 @@ var normalizePathname = (rawPathname, options = {}) => {
|
|
|
205
189
|
return result || "/";
|
|
206
190
|
};
|
|
207
191
|
|
|
208
|
-
// src/shared/utils/pathname/extract-pathname.ts
|
|
209
|
-
var extractPathname = ({
|
|
210
|
-
config,
|
|
211
|
-
pathname: rawPathname
|
|
212
|
-
}) => {
|
|
213
|
-
const { routing, defaultLocale } = config;
|
|
214
|
-
const { basePath, prefix } = routing;
|
|
215
|
-
const normalizedPathname = normalizePathname(rawPathname);
|
|
216
|
-
let prefixedPathname = normalizedPathname;
|
|
217
|
-
if (basePath && normalizedPathname.startsWith(basePath + "/")) {
|
|
218
|
-
prefixedPathname = normalizedPathname.slice(basePath.length) || "/";
|
|
219
|
-
} else if (basePath && normalizedPathname === basePath) {
|
|
220
|
-
prefixedPathname = "/";
|
|
221
|
-
}
|
|
222
|
-
const pathParts = prefixedPathname.split("/").filter(Boolean);
|
|
223
|
-
const maybeLocale = pathParts[0] || "";
|
|
224
|
-
const isLocalePrefixed = config.supportedLocales?.includes(maybeLocale);
|
|
225
|
-
let unprefixedPathname = prefixedPathname;
|
|
226
|
-
if (prefix === "all") {
|
|
227
|
-
if (isLocalePrefixed) {
|
|
228
|
-
unprefixedPathname = prefixedPathname.slice(maybeLocale.length + 1) || "/";
|
|
229
|
-
}
|
|
230
|
-
} else if (prefix === "except-default") {
|
|
231
|
-
if (maybeLocale && maybeLocale !== defaultLocale && isLocalePrefixed) {
|
|
232
|
-
unprefixedPathname = prefixedPathname.slice(maybeLocale.length + 1) || "/";
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return {
|
|
236
|
-
basePath,
|
|
237
|
-
prefixedPathname,
|
|
238
|
-
unprefixedPathname,
|
|
239
|
-
maybeLocale,
|
|
240
|
-
isLocalePrefixed: Boolean(isLocalePrefixed)
|
|
241
|
-
};
|
|
242
|
-
};
|
|
243
|
-
|
|
244
192
|
// src/shared/utils/pathname/standardize-pathname.ts
|
|
245
193
|
var standardizePathname = ({
|
|
246
194
|
config,
|
|
@@ -253,7 +201,7 @@ var standardizePathname = ({
|
|
|
253
201
|
PREFIX_PLACEHOLDER,
|
|
254
202
|
normalizePathname(pathname)
|
|
255
203
|
];
|
|
256
|
-
const standardizedPathname = parts.join("/").
|
|
204
|
+
const standardizedPathname = parts.join("/").replaceAll(/\/{2,}/g, "/");
|
|
257
205
|
return normalizePathname(standardizedPathname);
|
|
258
206
|
};
|
|
259
207
|
|
|
@@ -307,21 +255,6 @@ var IntorError = class extends Error {
|
|
|
307
255
|
}
|
|
308
256
|
};
|
|
309
257
|
|
|
310
|
-
// src/modules/messages/load-local-messages/utils/read-message-record-file.ts
|
|
311
|
-
var readMessageRecordFile = async (filePath, loggerOptions) => {
|
|
312
|
-
const fileName = path__default.default.basename(filePath, ".json");
|
|
313
|
-
const content = await fs__default.default.readFile(filePath, "utf-8");
|
|
314
|
-
const parsed = JSON.parse(content);
|
|
315
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
316
|
-
throw new IntorError({
|
|
317
|
-
id: loggerOptions.id,
|
|
318
|
-
code: "INTOR_INVALID_MESSAGE_FORMAT" /* INVALID_MESSAGE_FORMAT */,
|
|
319
|
-
message: "Invalid message format"
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
return { fileName, content: parsed };
|
|
323
|
-
};
|
|
324
|
-
|
|
325
258
|
// src/modules/messages/load-local-messages/load-namespace-group/parse-message-file.ts
|
|
326
259
|
var MAX_PATH_LENGTH = 260;
|
|
327
260
|
var parseMessageFile = async (filePath, loggerOptions) => {
|
|
@@ -342,9 +275,17 @@ var parseMessageFile = async (filePath, loggerOptions) => {
|
|
|
342
275
|
return null;
|
|
343
276
|
}
|
|
344
277
|
try {
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
278
|
+
const content = await fs__default.default.readFile(trimmedPath, "utf8");
|
|
279
|
+
const parsed = JSON.parse(content);
|
|
280
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
281
|
+
throw new IntorError({
|
|
282
|
+
id: loggerOptions.id,
|
|
283
|
+
code: "INTOR_INVALID_MESSAGE_FORMAT" /* INVALID_MESSAGE_FORMAT */,
|
|
284
|
+
message: "Invalid message format"
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
logger.trace("Message file loaded.", { filePath: trimmedPath });
|
|
288
|
+
return parsed;
|
|
348
289
|
} catch (error) {
|
|
349
290
|
logger.warn("Failed to parse message file.", {
|
|
350
291
|
filePath: trimmedPath,
|
|
@@ -438,7 +379,7 @@ var addToNamespaceGroup = ({
|
|
|
438
379
|
const filePathsSet = new Set(group.filePaths);
|
|
439
380
|
if (!filePathsSet.has(filePath)) {
|
|
440
381
|
filePathsSet.add(filePath);
|
|
441
|
-
group.filePaths =
|
|
382
|
+
group.filePaths = [...filePathsSet];
|
|
442
383
|
namespaceGroups.set(nsKey, group);
|
|
443
384
|
}
|
|
444
385
|
};
|
|
@@ -448,14 +389,15 @@ var traverseDirectory = async ({
|
|
|
448
389
|
options,
|
|
449
390
|
currentDirPath,
|
|
450
391
|
namespaceGroups,
|
|
451
|
-
namespacePathSegments
|
|
392
|
+
namespacePathSegments,
|
|
393
|
+
readdir = fs__default.default.readdir
|
|
452
394
|
}) => {
|
|
453
395
|
const { limit } = options;
|
|
454
396
|
const loggerOptions = options.logger || { id: "default" };
|
|
455
397
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
456
398
|
const logger = baseLogger.child({ scope: "traverse-directory" });
|
|
457
399
|
try {
|
|
458
|
-
const dirents = await
|
|
400
|
+
const dirents = await readdir(currentDirPath, { withFileTypes: true });
|
|
459
401
|
const dirPromises = dirents.map(
|
|
460
402
|
(dirent) => limit(async () => {
|
|
461
403
|
const filePath = path__default.default.join(currentDirPath, dirent.name);
|
|
@@ -471,7 +413,8 @@ var traverseDirectory = async ({
|
|
|
471
413
|
namespaceGroups,
|
|
472
414
|
currentDirPath: filePath,
|
|
473
415
|
namespacePathSegments: [...namespacePathSegments, dirent.name],
|
|
474
|
-
options
|
|
416
|
+
options,
|
|
417
|
+
readdir
|
|
475
418
|
});
|
|
476
419
|
}
|
|
477
420
|
}).catch((error) => {
|
|
@@ -546,7 +489,7 @@ var loadSingleLocale = async ({
|
|
|
546
489
|
namespaceGroups: [...namespaceGroups.entries()].map(([ns, val]) => ({
|
|
547
490
|
namespace: ns,
|
|
548
491
|
isAtRoot: val.isAtRoot,
|
|
549
|
-
|
|
492
|
+
fileCount: val.filePaths.length
|
|
550
493
|
}))
|
|
551
494
|
});
|
|
552
495
|
const namespaceGroupTasks = [...namespaceGroups.entries()].filter(
|
|
@@ -577,8 +520,8 @@ var loadLocaleWithFallback = async ({
|
|
|
577
520
|
}) => {
|
|
578
521
|
const baseLogger = getLogger({ ...loggerOptions });
|
|
579
522
|
const logger = baseLogger.child({ scope: "load-locale-with-fallback" });
|
|
580
|
-
const
|
|
581
|
-
for (const locale of
|
|
523
|
+
const candidateLocales = [targetLocale, ...fallbackLocales];
|
|
524
|
+
for (const locale of candidateLocales) {
|
|
582
525
|
try {
|
|
583
526
|
const validNamespaces = await loadSingleLocale({
|
|
584
527
|
basePath,
|
|
@@ -597,7 +540,7 @@ var loadLocaleWithFallback = async ({
|
|
|
597
540
|
}
|
|
598
541
|
}
|
|
599
542
|
logger.warn("All fallback locales failed.", {
|
|
600
|
-
attemptedLocales:
|
|
543
|
+
attemptedLocales: candidateLocales
|
|
601
544
|
});
|
|
602
545
|
return;
|
|
603
546
|
};
|
|
@@ -643,8 +586,8 @@ var loadLocalMessages = async ({
|
|
|
643
586
|
loggerOptions.id,
|
|
644
587
|
resolvedBasePath,
|
|
645
588
|
locale,
|
|
646
|
-
|
|
647
|
-
|
|
589
|
+
(fallbackLocales ?? []).toSorted().join(","),
|
|
590
|
+
(namespaces ?? []).toSorted().join(",")
|
|
648
591
|
]);
|
|
649
592
|
if (cache.enabled && key) {
|
|
650
593
|
const cached = await pool?.get(key);
|
|
@@ -789,8 +732,8 @@ var loadApiMessages = async ({
|
|
|
789
732
|
loggerOptions.id,
|
|
790
733
|
basePath,
|
|
791
734
|
locale,
|
|
792
|
-
|
|
793
|
-
|
|
735
|
+
(fallbackLocales ?? []).toSorted().join(","),
|
|
736
|
+
(namespaces ?? []).toSorted().join(",")
|
|
794
737
|
]);
|
|
795
738
|
if (cache.enabled && key) {
|
|
796
739
|
const cached = await pool?.get(key);
|
|
@@ -852,7 +795,7 @@ var loadMessages = async ({
|
|
|
852
795
|
);
|
|
853
796
|
return;
|
|
854
797
|
}
|
|
855
|
-
const { loader } = config;
|
|
798
|
+
const { id, loader, cache } = config;
|
|
856
799
|
const fallbackLocales = config.fallbackLocales[locale] || [];
|
|
857
800
|
const namespaces = resolveNamespaces({ config, pathname });
|
|
858
801
|
logger.debug("Namespaces ready for loading.", {
|
|
@@ -867,8 +810,8 @@ var loadMessages = async ({
|
|
|
867
810
|
locale,
|
|
868
811
|
fallbackLocales,
|
|
869
812
|
namespaces,
|
|
870
|
-
cache
|
|
871
|
-
logger: { id
|
|
813
|
+
cache,
|
|
814
|
+
logger: { id }
|
|
872
815
|
});
|
|
873
816
|
} else if (loader.type === "api") {
|
|
874
817
|
loadedMessages = await loadApiMessages({
|
|
@@ -876,7 +819,7 @@ var loadMessages = async ({
|
|
|
876
819
|
locale,
|
|
877
820
|
fallbackLocales,
|
|
878
821
|
namespaces,
|
|
879
|
-
logger: { id
|
|
822
|
+
logger: { id }
|
|
880
823
|
});
|
|
881
824
|
}
|
|
882
825
|
if (!loadedMessages || Object.keys(loadedMessages).length === 0) {
|
|
@@ -887,29 +830,23 @@ var loadMessages = async ({
|
|
|
887
830
|
|
|
888
831
|
// src/modules/tools/get-translator.ts
|
|
889
832
|
async function getTranslator(opts) {
|
|
890
|
-
const { config, locale, pathname = "", preKey } = opts;
|
|
833
|
+
const { config, locale, pathname = "", preKey, handlers } = opts;
|
|
891
834
|
const messages = await loadMessages({ config, locale, pathname });
|
|
892
835
|
const translator = new intorTranslator.Translator({
|
|
893
836
|
locale,
|
|
894
837
|
messages,
|
|
895
838
|
fallbackLocales: config.fallbackLocales,
|
|
896
839
|
loadingMessage: config.translator?.loadingMessage,
|
|
897
|
-
placeholder: config.translator?.placeholder
|
|
840
|
+
placeholder: config.translator?.placeholder,
|
|
841
|
+
handlers
|
|
898
842
|
});
|
|
899
|
-
const props = {
|
|
900
|
-
|
|
901
|
-
|
|
843
|
+
const props = { messages, locale };
|
|
844
|
+
const scoped = translator.scoped(preKey);
|
|
845
|
+
return {
|
|
846
|
+
...props,
|
|
847
|
+
hasKey: preKey ? scoped.hasKey : translator.hasKey,
|
|
848
|
+
t: preKey ? scoped.t : translator.t
|
|
902
849
|
};
|
|
903
|
-
if (preKey) {
|
|
904
|
-
const scoped = translator.scoped(preKey);
|
|
905
|
-
return { ...props, ...scoped };
|
|
906
|
-
} else {
|
|
907
|
-
return {
|
|
908
|
-
...props,
|
|
909
|
-
t: translator.t,
|
|
910
|
-
hasKey: translator.hasKey
|
|
911
|
-
};
|
|
912
|
-
}
|
|
913
850
|
}
|
|
914
851
|
|
|
915
852
|
// src/adapters/next/server/get-translator.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Level, NormalizerConfig, FormatterConfig, LoggerPreset } from 'logry/edge';
|
|
2
|
-
import { Locale,
|
|
2
|
+
import { Locale, LocaleMessages, FallbackLocalesMap, ScopedLeafKeys, LocalizedLeafKeys, Replacement, LocalizedNodeKeys } from 'intor-translator';
|
|
3
3
|
|
|
4
4
|
type CookieRawOptions = {
|
|
5
5
|
/** Completely disable cookie usage (no read, no write, no lookup by name) - default: false */
|
|
@@ -109,7 +109,7 @@ type WithLoader = {
|
|
|
109
109
|
};
|
|
110
110
|
type IntorResolvedConfig = (WithLoader | WithoutLoader) & {
|
|
111
111
|
readonly id: string;
|
|
112
|
-
readonly messages?:
|
|
112
|
+
readonly messages?: LocaleMessages;
|
|
113
113
|
readonly defaultLocale: Locale;
|
|
114
114
|
readonly fallbackLocales: FallbackLocalesMap;
|
|
115
115
|
readonly translator?: TranslatorOptions;
|
|
@@ -121,20 +121,34 @@ type IntorResolvedConfig = (WithLoader | WithoutLoader) & {
|
|
|
121
121
|
|
|
122
122
|
declare const PREFIX_PLACEHOLDER = "{locale}";
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Conditional type for generated types.
|
|
126
|
+
* - Returns `Then` if `IntorGeneratedTypes` exists, otherwise `Else`.
|
|
127
|
+
*/
|
|
124
128
|
type IfGen<Then, Else = never> = IntorGeneratedTypes extends void ? Else : Then;
|
|
129
|
+
/**
|
|
130
|
+
* Union of all configuration keys.
|
|
131
|
+
* - Defaults to `string` if `IntorGeneratedTypes` does not exist.
|
|
132
|
+
*/
|
|
125
133
|
type GenConfigKeys = IfGen<keyof IntorGeneratedTypes, string>;
|
|
126
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Configuration shape for a given config key.
|
|
136
|
+
* - If `IntorGeneratedTypes` is not defined, falls back to default shape.
|
|
137
|
+
* Otherwise, picks `Locales` and `Messages` according to the key.
|
|
138
|
+
*/
|
|
139
|
+
type GenConfig<CK extends GenConfigKeys = "__default__"> = IntorGeneratedTypes extends void ? {
|
|
127
140
|
Locales: string;
|
|
128
|
-
Messages:
|
|
129
|
-
} :
|
|
130
|
-
Locales: IntorGeneratedTypes[
|
|
141
|
+
Messages: LocaleMessages;
|
|
142
|
+
} : CK extends keyof IntorGeneratedTypes ? {
|
|
143
|
+
Locales: IntorGeneratedTypes[CK]["Locales"];
|
|
131
144
|
Messages: {
|
|
132
|
-
[K in IntorGeneratedTypes[
|
|
145
|
+
[K in IntorGeneratedTypes[CK]["Locales"]]: IntorGeneratedTypes[CK]["Messages"][typeof PREFIX_PLACEHOLDER];
|
|
133
146
|
};
|
|
134
147
|
} : never;
|
|
135
|
-
|
|
136
|
-
type
|
|
137
|
-
|
|
148
|
+
/** Extracts messages for a given config key */
|
|
149
|
+
type GenMessages<CK extends GenConfigKeys = "__default__"> = GenConfig<CK>["Messages"];
|
|
150
|
+
/** Extracts locales for a given config key */
|
|
151
|
+
type GenLocale<CK extends GenConfigKeys = "__default__"> = GenConfig<CK>["Locales"];
|
|
138
152
|
|
|
139
153
|
interface I18nContext {
|
|
140
154
|
locale: GenLocale;
|
|
@@ -146,24 +160,37 @@ interface I18nContext {
|
|
|
146
160
|
*
|
|
147
161
|
* Next.js adapter implementation: uses `next/headers` and `next/cookies`.
|
|
148
162
|
*/
|
|
149
|
-
declare const getI18nContext: (config: IntorResolvedConfig) => Promise<I18nContext>;
|
|
163
|
+
declare const getI18nContext: <CK extends string = "__default__">(config: IntorResolvedConfig) => Promise<I18nContext>;
|
|
150
164
|
|
|
151
|
-
|
|
152
|
-
interface TranslatorBaseProps<M> {
|
|
165
|
+
/** Base properties shared by all translator instances. */
|
|
166
|
+
interface TranslatorBaseProps<M = unknown> {
|
|
167
|
+
/** `messages`: The message object containing all translations. */
|
|
153
168
|
messages: M;
|
|
154
|
-
locale
|
|
169
|
+
/** Current locale in use. */
|
|
170
|
+
locale: Locale<M>;
|
|
155
171
|
}
|
|
156
|
-
|
|
172
|
+
/** Properties specific to client-side translator behavior. */
|
|
173
|
+
interface TranslatorClientProps<M = unknown> {
|
|
174
|
+
/** `isLoading`: Indicates whether translations are currently loading. */
|
|
157
175
|
isLoading: boolean;
|
|
158
|
-
setLocale
|
|
176
|
+
/** `setLocale`: Function to update the current locale. */
|
|
177
|
+
setLocale: (locale: Locale<M>) => void;
|
|
159
178
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Conditional key type for TranslatorInstance.
|
|
181
|
+
* - Resolves to `ScopedLeafKeys` if a pre-key `PK` is provided,
|
|
182
|
+
* otherwise resolves to `LocalizedLeafKeys`.
|
|
183
|
+
*/
|
|
184
|
+
type Key<M, PK> = IfGen<PK extends string ? ScopedLeafKeys<M, PK> : LocalizedLeafKeys<M>, string>;
|
|
185
|
+
/**
|
|
186
|
+
* Translator instance type.
|
|
187
|
+
* Combines base props, client props, and core translation methods.
|
|
188
|
+
*/
|
|
189
|
+
type TranslatorInstance<M, PK extends string | undefined = undefined> = {
|
|
190
|
+
/** Check if a given key exists in the messages. */
|
|
191
|
+
hasKey: (key?: Key<M, PK>, targetLocale?: Locale<M>) => boolean;
|
|
192
|
+
/** Translate a given key into its string representation. */
|
|
193
|
+
t: <Result = string>(key?: Key<M, PK>, replacements?: Replacement) => Result;
|
|
167
194
|
} & TranslatorBaseProps<M> & TranslatorClientProps<M>;
|
|
168
195
|
|
|
169
196
|
/**
|
|
@@ -175,7 +202,7 @@ type ScopedTranslatorInstance<M, K extends string> = {
|
|
|
175
202
|
* - Supports optional `preKey` to create a scoped translator for nested translation keys.
|
|
176
203
|
* - Allows passing additional `TranslateConfig` options to the underlying translator.
|
|
177
204
|
*/
|
|
178
|
-
declare function getTranslator<
|
|
179
|
-
declare function getTranslator<
|
|
205
|
+
declare function getTranslator<CK extends GenConfigKeys = "__default__">(config: IntorResolvedConfig): Promise<TranslatorInstance<GenMessages<CK>>>;
|
|
206
|
+
declare function getTranslator<CK extends GenConfigKeys = "__default__", PK extends string = LocalizedNodeKeys<GenMessages<CK>>>(config: IntorResolvedConfig, preKey: IfGen<PK, string>): Promise<TranslatorInstance<GenMessages<CK>, PK>>;
|
|
180
207
|
|
|
181
208
|
export { getI18nContext, getTranslator };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Level, NormalizerConfig, FormatterConfig, LoggerPreset } from 'logry/edge';
|
|
2
|
-
import { Locale,
|
|
2
|
+
import { Locale, LocaleMessages, FallbackLocalesMap, ScopedLeafKeys, LocalizedLeafKeys, Replacement, LocalizedNodeKeys } from 'intor-translator';
|
|
3
3
|
|
|
4
4
|
type CookieRawOptions = {
|
|
5
5
|
/** Completely disable cookie usage (no read, no write, no lookup by name) - default: false */
|
|
@@ -109,7 +109,7 @@ type WithLoader = {
|
|
|
109
109
|
};
|
|
110
110
|
type IntorResolvedConfig = (WithLoader | WithoutLoader) & {
|
|
111
111
|
readonly id: string;
|
|
112
|
-
readonly messages?:
|
|
112
|
+
readonly messages?: LocaleMessages;
|
|
113
113
|
readonly defaultLocale: Locale;
|
|
114
114
|
readonly fallbackLocales: FallbackLocalesMap;
|
|
115
115
|
readonly translator?: TranslatorOptions;
|
|
@@ -121,20 +121,34 @@ type IntorResolvedConfig = (WithLoader | WithoutLoader) & {
|
|
|
121
121
|
|
|
122
122
|
declare const PREFIX_PLACEHOLDER = "{locale}";
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Conditional type for generated types.
|
|
126
|
+
* - Returns `Then` if `IntorGeneratedTypes` exists, otherwise `Else`.
|
|
127
|
+
*/
|
|
124
128
|
type IfGen<Then, Else = never> = IntorGeneratedTypes extends void ? Else : Then;
|
|
129
|
+
/**
|
|
130
|
+
* Union of all configuration keys.
|
|
131
|
+
* - Defaults to `string` if `IntorGeneratedTypes` does not exist.
|
|
132
|
+
*/
|
|
125
133
|
type GenConfigKeys = IfGen<keyof IntorGeneratedTypes, string>;
|
|
126
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Configuration shape for a given config key.
|
|
136
|
+
* - If `IntorGeneratedTypes` is not defined, falls back to default shape.
|
|
137
|
+
* Otherwise, picks `Locales` and `Messages` according to the key.
|
|
138
|
+
*/
|
|
139
|
+
type GenConfig<CK extends GenConfigKeys = "__default__"> = IntorGeneratedTypes extends void ? {
|
|
127
140
|
Locales: string;
|
|
128
|
-
Messages:
|
|
129
|
-
} :
|
|
130
|
-
Locales: IntorGeneratedTypes[
|
|
141
|
+
Messages: LocaleMessages;
|
|
142
|
+
} : CK extends keyof IntorGeneratedTypes ? {
|
|
143
|
+
Locales: IntorGeneratedTypes[CK]["Locales"];
|
|
131
144
|
Messages: {
|
|
132
|
-
[K in IntorGeneratedTypes[
|
|
145
|
+
[K in IntorGeneratedTypes[CK]["Locales"]]: IntorGeneratedTypes[CK]["Messages"][typeof PREFIX_PLACEHOLDER];
|
|
133
146
|
};
|
|
134
147
|
} : never;
|
|
135
|
-
|
|
136
|
-
type
|
|
137
|
-
|
|
148
|
+
/** Extracts messages for a given config key */
|
|
149
|
+
type GenMessages<CK extends GenConfigKeys = "__default__"> = GenConfig<CK>["Messages"];
|
|
150
|
+
/** Extracts locales for a given config key */
|
|
151
|
+
type GenLocale<CK extends GenConfigKeys = "__default__"> = GenConfig<CK>["Locales"];
|
|
138
152
|
|
|
139
153
|
interface I18nContext {
|
|
140
154
|
locale: GenLocale;
|
|
@@ -146,24 +160,37 @@ interface I18nContext {
|
|
|
146
160
|
*
|
|
147
161
|
* Next.js adapter implementation: uses `next/headers` and `next/cookies`.
|
|
148
162
|
*/
|
|
149
|
-
declare const getI18nContext: (config: IntorResolvedConfig) => Promise<I18nContext>;
|
|
163
|
+
declare const getI18nContext: <CK extends string = "__default__">(config: IntorResolvedConfig) => Promise<I18nContext>;
|
|
150
164
|
|
|
151
|
-
|
|
152
|
-
interface TranslatorBaseProps<M> {
|
|
165
|
+
/** Base properties shared by all translator instances. */
|
|
166
|
+
interface TranslatorBaseProps<M = unknown> {
|
|
167
|
+
/** `messages`: The message object containing all translations. */
|
|
153
168
|
messages: M;
|
|
154
|
-
locale
|
|
169
|
+
/** Current locale in use. */
|
|
170
|
+
locale: Locale<M>;
|
|
155
171
|
}
|
|
156
|
-
|
|
172
|
+
/** Properties specific to client-side translator behavior. */
|
|
173
|
+
interface TranslatorClientProps<M = unknown> {
|
|
174
|
+
/** `isLoading`: Indicates whether translations are currently loading. */
|
|
157
175
|
isLoading: boolean;
|
|
158
|
-
setLocale
|
|
176
|
+
/** `setLocale`: Function to update the current locale. */
|
|
177
|
+
setLocale: (locale: Locale<M>) => void;
|
|
159
178
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Conditional key type for TranslatorInstance.
|
|
181
|
+
* - Resolves to `ScopedLeafKeys` if a pre-key `PK` is provided,
|
|
182
|
+
* otherwise resolves to `LocalizedLeafKeys`.
|
|
183
|
+
*/
|
|
184
|
+
type Key<M, PK> = IfGen<PK extends string ? ScopedLeafKeys<M, PK> : LocalizedLeafKeys<M>, string>;
|
|
185
|
+
/**
|
|
186
|
+
* Translator instance type.
|
|
187
|
+
* Combines base props, client props, and core translation methods.
|
|
188
|
+
*/
|
|
189
|
+
type TranslatorInstance<M, PK extends string | undefined = undefined> = {
|
|
190
|
+
/** Check if a given key exists in the messages. */
|
|
191
|
+
hasKey: (key?: Key<M, PK>, targetLocale?: Locale<M>) => boolean;
|
|
192
|
+
/** Translate a given key into its string representation. */
|
|
193
|
+
t: <Result = string>(key?: Key<M, PK>, replacements?: Replacement) => Result;
|
|
167
194
|
} & TranslatorBaseProps<M> & TranslatorClientProps<M>;
|
|
168
195
|
|
|
169
196
|
/**
|
|
@@ -175,7 +202,7 @@ type ScopedTranslatorInstance<M, K extends string> = {
|
|
|
175
202
|
* - Supports optional `preKey` to create a scoped translator for nested translation keys.
|
|
176
203
|
* - Allows passing additional `TranslateConfig` options to the underlying translator.
|
|
177
204
|
*/
|
|
178
|
-
declare function getTranslator<
|
|
179
|
-
declare function getTranslator<
|
|
205
|
+
declare function getTranslator<CK extends GenConfigKeys = "__default__">(config: IntorResolvedConfig): Promise<TranslatorInstance<GenMessages<CK>>>;
|
|
206
|
+
declare function getTranslator<CK extends GenConfigKeys = "__default__", PK extends string = LocalizedNodeKeys<GenMessages<CK>>>(config: IntorResolvedConfig, preKey: IfGen<PK, string>): Promise<TranslatorInstance<GenMessages<CK>, PK>>;
|
|
180
207
|
|
|
181
208
|
export { getI18nContext, getTranslator };
|