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.
@@ -50,7 +50,7 @@ function getLogger({
50
50
  });
51
51
  pool.set(id, logger);
52
52
  if (pool.size > 1e3) {
53
- const keys = Array.from(pool.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 (!key) return null;
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
- routeNamespaces = {},
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 defaultNamespaces = routeNamespaces.default ?? [];
102
- const exactMatchNamespaces = routeNamespaces[standardizedPathname] ?? routeNamespaces[placeholderRemovedPathname];
103
- if (exactMatchNamespaces) {
104
- return [...defaultNamespaces, ...exactMatchNamespaces];
105
- }
106
- let bestMatch = "";
107
- let bestNamespaces;
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
- if (basePath.length > bestMatch.length) {
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
- const matchedNamespaces = bestNamespaces ?? routeNamespaces["/*"] ?? fallbackNamespaces ?? [];
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
- }).sort((a, b) => b.q - a.q).find(({ lang }) => supportedLocalesSet.has(lang))?.lang;
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.charCodeAt(start) <= 32) start++;
183
- while (end >= start && rawPathname.charCodeAt(end) <= 32) end--;
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
- if (hasSlash || result === "") {
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("/").replace(/\/{2,}/g, "/");
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 { content } = await readMessageRecordFile(trimmedPath, loggerOptions);
346
- logger.trace(`Message file loaded.`, { filePath: trimmedPath });
347
- return content;
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 = Array.from(filePathsSet);
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 fs__default.default.readdir(currentDirPath, { withFileTypes: true });
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
- fileCounts: val.filePaths.length
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 localesToTry = [targetLocale, ...fallbackLocales];
581
- for (const locale of localesToTry) {
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: localesToTry
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
- [...fallbackLocales ?? []].sort().join(","),
647
- [...namespaces ?? []].sort().join(",")
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
- [...fallbackLocales ?? []].sort().join(","),
793
- [...namespaces ?? []].sort().join(",")
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: config.cache,
871
- logger: { id: config.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: config.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
- messages,
901
- locale
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, LocaleNamespaceMessages, FallbackLocalesMap, InferTranslatorKey, LocaleKey, Replacement, RichReplacement, NodeKeys, UnionLocaleMessages, ScopedLeafKeys } from 'intor-translator';
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?: LocaleNamespaceMessages;
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
- type GenConfig<C extends GenConfigKeys = "__default__"> = IntorGeneratedTypes extends void ? {
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: LocaleNamespaceMessages;
129
- } : C extends keyof IntorGeneratedTypes ? {
130
- Locales: IntorGeneratedTypes[C]["Locales"];
141
+ Messages: LocaleMessages;
142
+ } : CK extends keyof IntorGeneratedTypes ? {
143
+ Locales: IntorGeneratedTypes[CK]["Locales"];
131
144
  Messages: {
132
- [K in IntorGeneratedTypes[C]["Locales"]]: IntorGeneratedTypes[C]["Messages"][typeof PREFIX_PLACEHOLDER];
145
+ [K in IntorGeneratedTypes[CK]["Locales"]]: IntorGeneratedTypes[CK]["Messages"][typeof PREFIX_PLACEHOLDER];
133
146
  };
134
147
  } : never;
135
- type GenMessages<C extends GenConfigKeys = "__default__"> = GenConfig<C>["Messages"];
136
- type GenLocaleFallback = Locale;
137
- type GenLocale<Config extends string = "__default__"> = Config extends keyof IntorGeneratedTypes ? IntorGeneratedTypes[Config]["Locales"] : GenLocaleFallback;
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
- type PreKey<C extends GenConfigKeys = "__default__"> = NodeKeys<UnionLocaleMessages<GenMessages<C>>>;
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: LocaleKey<M>;
169
+ /** Current locale in use. */
170
+ locale: Locale<M>;
155
171
  }
156
- interface TranslatorClientProps<M> {
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: (locale: LocaleKey<M>) => void;
176
+ /** `setLocale`: Function to update the current locale. */
177
+ setLocale: (locale: Locale<M>) => void;
159
178
  }
160
- type TranslatorInstance<M> = {
161
- hasKey: (key?: IfGen<InferTranslatorKey<M>, string>, targetLocale?: LocaleKey<M> | undefined) => boolean;
162
- t: <Result = string>(key?: IfGen<InferTranslatorKey<M>, string>, replacements?: Replacement | RichReplacement) => Result;
163
- } & TranslatorBaseProps<M> & TranslatorClientProps<M>;
164
- type ScopedTranslatorInstance<M, K extends string> = {
165
- hasKey: (key?: IfGen<ScopedLeafKeys<M, K> & string, string>, targetLocale?: LocaleKey<M>) => boolean;
166
- t: (key?: IfGen<ScopedLeafKeys<M, K> & string, string>, replacements?: Replacement | RichReplacement) => string;
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<C extends GenConfigKeys = "__default__">(config: IntorResolvedConfig): Promise<TranslatorInstance<GenMessages<C>>>;
179
- declare function getTranslator<C extends GenConfigKeys = "__default__", K extends PreKey<C> = PreKey<C>>(config: IntorResolvedConfig, preKey?: K): Promise<ScopedTranslatorInstance<GenMessages<C>, K>>;
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, LocaleNamespaceMessages, FallbackLocalesMap, InferTranslatorKey, LocaleKey, Replacement, RichReplacement, NodeKeys, UnionLocaleMessages, ScopedLeafKeys } from 'intor-translator';
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?: LocaleNamespaceMessages;
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
- type GenConfig<C extends GenConfigKeys = "__default__"> = IntorGeneratedTypes extends void ? {
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: LocaleNamespaceMessages;
129
- } : C extends keyof IntorGeneratedTypes ? {
130
- Locales: IntorGeneratedTypes[C]["Locales"];
141
+ Messages: LocaleMessages;
142
+ } : CK extends keyof IntorGeneratedTypes ? {
143
+ Locales: IntorGeneratedTypes[CK]["Locales"];
131
144
  Messages: {
132
- [K in IntorGeneratedTypes[C]["Locales"]]: IntorGeneratedTypes[C]["Messages"][typeof PREFIX_PLACEHOLDER];
145
+ [K in IntorGeneratedTypes[CK]["Locales"]]: IntorGeneratedTypes[CK]["Messages"][typeof PREFIX_PLACEHOLDER];
133
146
  };
134
147
  } : never;
135
- type GenMessages<C extends GenConfigKeys = "__default__"> = GenConfig<C>["Messages"];
136
- type GenLocaleFallback = Locale;
137
- type GenLocale<Config extends string = "__default__"> = Config extends keyof IntorGeneratedTypes ? IntorGeneratedTypes[Config]["Locales"] : GenLocaleFallback;
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
- type PreKey<C extends GenConfigKeys = "__default__"> = NodeKeys<UnionLocaleMessages<GenMessages<C>>>;
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: LocaleKey<M>;
169
+ /** Current locale in use. */
170
+ locale: Locale<M>;
155
171
  }
156
- interface TranslatorClientProps<M> {
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: (locale: LocaleKey<M>) => void;
176
+ /** `setLocale`: Function to update the current locale. */
177
+ setLocale: (locale: Locale<M>) => void;
159
178
  }
160
- type TranslatorInstance<M> = {
161
- hasKey: (key?: IfGen<InferTranslatorKey<M>, string>, targetLocale?: LocaleKey<M> | undefined) => boolean;
162
- t: <Result = string>(key?: IfGen<InferTranslatorKey<M>, string>, replacements?: Replacement | RichReplacement) => Result;
163
- } & TranslatorBaseProps<M> & TranslatorClientProps<M>;
164
- type ScopedTranslatorInstance<M, K extends string> = {
165
- hasKey: (key?: IfGen<ScopedLeafKeys<M, K> & string, string>, targetLocale?: LocaleKey<M>) => boolean;
166
- t: (key?: IfGen<ScopedLeafKeys<M, K> & string, string>, replacements?: Replacement | RichReplacement) => string;
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<C extends GenConfigKeys = "__default__">(config: IntorResolvedConfig): Promise<TranslatorInstance<GenMessages<C>>>;
179
- declare function getTranslator<C extends GenConfigKeys = "__default__", K extends PreKey<C> = PreKey<C>>(config: IntorResolvedConfig, preKey?: K): Promise<ScopedTranslatorInstance<GenMessages<C>, K>>;
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 };