intor-translator 1.1.2 → 1.1.3

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/index.cjs CHANGED
@@ -39,9 +39,6 @@ var hasKey = ({
39
39
  }) => {
40
40
  const messages = messagesRef.current;
41
41
  const locale = localeRef.current;
42
- if (!messages) {
43
- throw new Error("[intor-translator] 'messages' is required");
44
- }
45
42
  const candidateLocales = resolveCandidateLocales(targetLocale || locale);
46
43
  const message = findMessageInLocales({ messages, candidateLocales, key });
47
44
  return !!message;
@@ -78,9 +75,6 @@ var translate = ({
78
75
  const messages = messagesRef.current;
79
76
  const locale = localeRef.current;
80
77
  const isLoading = isLoadingRef.current;
81
- if (!messages) {
82
- throw new Error("[intor-translator] 'messages' is required");
83
- }
84
78
  const { fallbackLocales, loadingMessage, placeholder, handlers } = translateConfig;
85
79
  const { formatHandler, loadingHandler, missingHandler } = handlers || {};
86
80
  const candidateLocales = resolveCandidateLocales(locale, fallbackLocales);
@@ -104,11 +98,16 @@ var translate = ({
104
98
 
105
99
  // src/translators/base-translator/base-translator.ts
106
100
  var BaseTranslator = class {
101
+ /** Current messages for translation */
102
+ messagesRef;
103
+ /** Current active locale */
104
+ localeRef;
105
+ /** Current loading state */
106
+ isLoadingRef;
107
107
  constructor(options) {
108
- /** Current messages for translation, updatable at runtime */
109
- this.messagesRef = { current: void 0 };
110
- this.messagesRef = { current: options.messages };
108
+ this.messagesRef = { current: options.messages ?? {} };
111
109
  this.localeRef = { current: options.locale };
110
+ this.isLoadingRef = { current: options.isLoading ?? false };
112
111
  }
113
112
  /** Get messages. */
114
113
  get messages() {
@@ -118,6 +117,10 @@ var BaseTranslator = class {
118
117
  get locale() {
119
118
  return this.localeRef.current;
120
119
  }
120
+ /** Get the current loading state. */
121
+ get isLoading() {
122
+ return this.isLoadingRef.current;
123
+ }
121
124
  /**
122
125
  * Replace messages with new ones.
123
126
  *
@@ -135,42 +138,39 @@ var BaseTranslator = class {
135
138
  setLocale(newLocale) {
136
139
  this.localeRef.current = newLocale;
137
140
  }
141
+ /** Set the loading state. */
142
+ setLoading(state) {
143
+ this.isLoadingRef.current = state;
144
+ }
138
145
  };
139
146
 
140
147
  // src/translators/core-translator/core-translator.ts
141
148
  var CoreTranslator = class extends BaseTranslator {
149
+ options;
142
150
  constructor(options) {
143
151
  super({ locale: options.locale, messages: options.messages });
144
- this.isLoadingRef = { current: false };
145
- /** Check if a key exists in the specified locale or current locale. */
146
- this.hasKey = (key, targetLocale) => {
147
- return hasKey({
148
- messagesRef: this.messagesRef,
149
- localeRef: this.localeRef,
150
- key,
151
- targetLocale
152
- });
153
- };
154
- this.t = (key, replacements) => {
155
- return translate({
156
- messagesRef: this.messagesRef,
157
- localeRef: this.localeRef,
158
- isLoadingRef: this.isLoadingRef,
159
- translateConfig: this.options,
160
- key,
161
- replacements
162
- });
163
- };
164
152
  this.options = options;
165
153
  }
166
- /** Get the current loading state. */
167
- get isLoading() {
168
- return this.isLoadingRef.current;
169
- }
170
- /** Set the loading state. */
171
- setLoading(state) {
172
- this.isLoadingRef.current = state;
173
- }
154
+ /** Check if a key exists in the specified locale or current locale. */
155
+ hasKey = (key, targetLocale) => {
156
+ return hasKey({
157
+ messagesRef: this.messagesRef,
158
+ localeRef: this.localeRef,
159
+ key,
160
+ targetLocale
161
+ });
162
+ };
163
+ /** Get the translated message for a key, with optional replacements. */
164
+ t = (key, replacements) => {
165
+ return translate({
166
+ messagesRef: this.messagesRef,
167
+ localeRef: this.localeRef,
168
+ isLoadingRef: this.isLoadingRef,
169
+ translateConfig: this.options,
170
+ key,
171
+ replacements
172
+ });
173
+ };
174
174
  };
175
175
 
176
176
  // src/utils/get-full-key.ts
@@ -185,6 +185,7 @@ var ScopeTranslator = class extends CoreTranslator {
185
185
  constructor(options) {
186
186
  super(options);
187
187
  }
188
+ /** Create a scoped translator with a prefix key, providing `t` and `hasKey` for nested keys. */
188
189
  scoped(preKey) {
189
190
  return {
190
191
  hasKey: (key, targetLocale) => {
package/dist/index.d.cts CHANGED
@@ -21,7 +21,7 @@ type NestedMessage = string | {
21
21
  * Messages grouped by locale.
22
22
  * Used to structure all available messages for multiple locales.
23
23
  *
24
- * - Each key is a locale string, e.g., "en" or "zh-TW".
24
+ * - The root-level keys are locale identifiers, e.g., "en" or "zh-TW".
25
25
  * - Each value is a `NestedMessage`, allowing for deeply nested message objects.
26
26
  *
27
27
  * @example
@@ -260,7 +260,7 @@ type ScopedLeafKeys<M, PK extends string, L extends keyof M | "union" = "union",
260
260
  * ```
261
261
  */
262
262
  type MessagesRef<M = unknown> = {
263
- current?: Readonly<M>;
263
+ current: Readonly<M>;
264
264
  };
265
265
  /**
266
266
  * A ref object holding the currently selected locale.
@@ -356,18 +356,26 @@ interface BaseTranslatorOptions<M = unknown> {
356
356
  * - If `messages` is typed as `LocaleMessages`, this can be inferred automatically.
357
357
  */
358
358
  locale: Locale<M>;
359
+ /**
360
+ * Indicates whether the translator is in a loading state.
361
+ */
362
+ isLoading?: boolean;
359
363
  }
360
364
 
361
365
  declare class BaseTranslator<M = unknown> {
362
- /** Current messages for translation, updatable at runtime */
366
+ /** Current messages for translation */
363
367
  protected messagesRef: MessagesRef<M>;
364
- /** Current active locale, can be changed dynamically */
368
+ /** Current active locale */
365
369
  protected localeRef: LocaleRef<M>;
370
+ /** Current loading state */
371
+ protected isLoadingRef: IsLoadingRef;
366
372
  constructor(options: BaseTranslatorOptions<M>);
367
373
  /** Get messages. */
368
374
  get messages(): M | undefined;
369
375
  /** Get the current active locale. */
370
376
  get locale(): Locale<M>;
377
+ /** Get the current loading state. */
378
+ get isLoading(): boolean;
371
379
  /**
372
380
  * Replace messages with new ones.
373
381
  *
@@ -381,6 +389,8 @@ declare class BaseTranslator<M = unknown> {
381
389
  * - Note: Unlike `setMessages`, the locale structure cannot be changed at runtime.
382
390
  */
383
391
  setLocale(newLocale: Locale<M>): void;
392
+ /** Set the loading state. */
393
+ setLoading(state: boolean): void;
384
394
  }
385
395
 
386
396
  interface CoreTranslatorOptions<M> extends BaseTranslatorOptions<M>, TranslateConfig<M> {
@@ -388,14 +398,10 @@ interface CoreTranslatorOptions<M> extends BaseTranslatorOptions<M>, TranslateCo
388
398
 
389
399
  declare class CoreTranslator<M = unknown, L extends keyof M | "union" = "union"> extends BaseTranslator<M> {
390
400
  protected options: CoreTranslatorOptions<M>;
391
- protected isLoadingRef: IsLoadingRef;
392
401
  constructor(options: CoreTranslatorOptions<M>);
393
- /** Get the current loading state. */
394
- get isLoading(): boolean;
395
- /** Set the loading state. */
396
- setLoading(state: boolean): void;
397
402
  /** Check if a key exists in the specified locale or current locale. */
398
403
  hasKey: <K = LocalizedLeafKeys<M, L>>(key: K, targetLocale?: Locale<M>) => boolean;
404
+ /** Get the translated message for a key, with optional replacements. */
399
405
  t: <Result = string, K = LocalizedLeafKeys<M, L>>(key: K, replacements?: Replacement) => Result;
400
406
  }
401
407
 
@@ -407,7 +413,8 @@ type ScopeTranslatorMethods<M, L extends keyof M | "union" = "union", K = Locali
407
413
 
408
414
  declare class ScopeTranslator<M = unknown, L extends keyof M | "union" = "union"> extends CoreTranslator<M> {
409
415
  constructor(options: ScopeTranslatorOptions<M>);
416
+ /** Create a scoped translator with a prefix key, providing `t` and `hasKey` for nested keys. */
410
417
  scoped<PK extends LocalizedNodeKeys<M, L> | undefined = undefined>(preKey?: PK): PK extends string ? ScopeTranslatorMethods<M, L, ScopedLeafKeys<M, PK, L>> : ScopeTranslatorMethods<M, L>;
411
418
  }
412
419
 
413
- export { type FallbackLocalesMap, type FormatHandler, type IsLoadingRef, type LeafKeys, type LoadingHandler, type Locale, type LocaleMessages, type LocaleRef, type LocalizedLeafKeys, type LocalizedMessagesUnion, type LocalizedNodeKeys, type MessagesRef, type MissingHandler, type NestedMessage, type NodeKeys, type Replacement, ScopeTranslator, type ScopeTranslatorMethods, type ScopeTranslatorOptions, type ScopedLeafKeys, type TranslateHandlerContext, type TranslateHandlers, ScopeTranslator as Translator };
420
+ export { type DefaultDepth, type FallbackLocalesMap, type FormatHandler, type IsLoadingRef, type LeafKeys, type LoadingHandler, type Locale, type LocaleMessages, type LocaleRef, type LocalizedLeafKeys, type LocalizedMessagesUnion, type LocalizedNodeKeys, type MessagesRef, type MissingHandler, type NestedMessage, type NodeKeys, type Replacement, ScopeTranslator, type ScopeTranslatorMethods, type ScopeTranslatorOptions, type ScopedLeafKeys, type TranslateHandlerContext, type TranslateHandlers, ScopeTranslator as Translator };
package/dist/index.d.ts CHANGED
@@ -21,7 +21,7 @@ type NestedMessage = string | {
21
21
  * Messages grouped by locale.
22
22
  * Used to structure all available messages for multiple locales.
23
23
  *
24
- * - Each key is a locale string, e.g., "en" or "zh-TW".
24
+ * - The root-level keys are locale identifiers, e.g., "en" or "zh-TW".
25
25
  * - Each value is a `NestedMessage`, allowing for deeply nested message objects.
26
26
  *
27
27
  * @example
@@ -260,7 +260,7 @@ type ScopedLeafKeys<M, PK extends string, L extends keyof M | "union" = "union",
260
260
  * ```
261
261
  */
262
262
  type MessagesRef<M = unknown> = {
263
- current?: Readonly<M>;
263
+ current: Readonly<M>;
264
264
  };
265
265
  /**
266
266
  * A ref object holding the currently selected locale.
@@ -356,18 +356,26 @@ interface BaseTranslatorOptions<M = unknown> {
356
356
  * - If `messages` is typed as `LocaleMessages`, this can be inferred automatically.
357
357
  */
358
358
  locale: Locale<M>;
359
+ /**
360
+ * Indicates whether the translator is in a loading state.
361
+ */
362
+ isLoading?: boolean;
359
363
  }
360
364
 
361
365
  declare class BaseTranslator<M = unknown> {
362
- /** Current messages for translation, updatable at runtime */
366
+ /** Current messages for translation */
363
367
  protected messagesRef: MessagesRef<M>;
364
- /** Current active locale, can be changed dynamically */
368
+ /** Current active locale */
365
369
  protected localeRef: LocaleRef<M>;
370
+ /** Current loading state */
371
+ protected isLoadingRef: IsLoadingRef;
366
372
  constructor(options: BaseTranslatorOptions<M>);
367
373
  /** Get messages. */
368
374
  get messages(): M | undefined;
369
375
  /** Get the current active locale. */
370
376
  get locale(): Locale<M>;
377
+ /** Get the current loading state. */
378
+ get isLoading(): boolean;
371
379
  /**
372
380
  * Replace messages with new ones.
373
381
  *
@@ -381,6 +389,8 @@ declare class BaseTranslator<M = unknown> {
381
389
  * - Note: Unlike `setMessages`, the locale structure cannot be changed at runtime.
382
390
  */
383
391
  setLocale(newLocale: Locale<M>): void;
392
+ /** Set the loading state. */
393
+ setLoading(state: boolean): void;
384
394
  }
385
395
 
386
396
  interface CoreTranslatorOptions<M> extends BaseTranslatorOptions<M>, TranslateConfig<M> {
@@ -388,14 +398,10 @@ interface CoreTranslatorOptions<M> extends BaseTranslatorOptions<M>, TranslateCo
388
398
 
389
399
  declare class CoreTranslator<M = unknown, L extends keyof M | "union" = "union"> extends BaseTranslator<M> {
390
400
  protected options: CoreTranslatorOptions<M>;
391
- protected isLoadingRef: IsLoadingRef;
392
401
  constructor(options: CoreTranslatorOptions<M>);
393
- /** Get the current loading state. */
394
- get isLoading(): boolean;
395
- /** Set the loading state. */
396
- setLoading(state: boolean): void;
397
402
  /** Check if a key exists in the specified locale or current locale. */
398
403
  hasKey: <K = LocalizedLeafKeys<M, L>>(key: K, targetLocale?: Locale<M>) => boolean;
404
+ /** Get the translated message for a key, with optional replacements. */
399
405
  t: <Result = string, K = LocalizedLeafKeys<M, L>>(key: K, replacements?: Replacement) => Result;
400
406
  }
401
407
 
@@ -407,7 +413,8 @@ type ScopeTranslatorMethods<M, L extends keyof M | "union" = "union", K = Locali
407
413
 
408
414
  declare class ScopeTranslator<M = unknown, L extends keyof M | "union" = "union"> extends CoreTranslator<M> {
409
415
  constructor(options: ScopeTranslatorOptions<M>);
416
+ /** Create a scoped translator with a prefix key, providing `t` and `hasKey` for nested keys. */
410
417
  scoped<PK extends LocalizedNodeKeys<M, L> | undefined = undefined>(preKey?: PK): PK extends string ? ScopeTranslatorMethods<M, L, ScopedLeafKeys<M, PK, L>> : ScopeTranslatorMethods<M, L>;
411
418
  }
412
419
 
413
- export { type FallbackLocalesMap, type FormatHandler, type IsLoadingRef, type LeafKeys, type LoadingHandler, type Locale, type LocaleMessages, type LocaleRef, type LocalizedLeafKeys, type LocalizedMessagesUnion, type LocalizedNodeKeys, type MessagesRef, type MissingHandler, type NestedMessage, type NodeKeys, type Replacement, ScopeTranslator, type ScopeTranslatorMethods, type ScopeTranslatorOptions, type ScopedLeafKeys, type TranslateHandlerContext, type TranslateHandlers, ScopeTranslator as Translator };
420
+ export { type DefaultDepth, type FallbackLocalesMap, type FormatHandler, type IsLoadingRef, type LeafKeys, type LoadingHandler, type Locale, type LocaleMessages, type LocaleRef, type LocalizedLeafKeys, type LocalizedMessagesUnion, type LocalizedNodeKeys, type MessagesRef, type MissingHandler, type NestedMessage, type NodeKeys, type Replacement, ScopeTranslator, type ScopeTranslatorMethods, type ScopeTranslatorOptions, type ScopedLeafKeys, type TranslateHandlerContext, type TranslateHandlers, ScopeTranslator as Translator };
package/dist/index.js CHANGED
@@ -37,9 +37,6 @@ var hasKey = ({
37
37
  }) => {
38
38
  const messages = messagesRef.current;
39
39
  const locale = localeRef.current;
40
- if (!messages) {
41
- throw new Error("[intor-translator] 'messages' is required");
42
- }
43
40
  const candidateLocales = resolveCandidateLocales(targetLocale || locale);
44
41
  const message = findMessageInLocales({ messages, candidateLocales, key });
45
42
  return !!message;
@@ -76,9 +73,6 @@ var translate = ({
76
73
  const messages = messagesRef.current;
77
74
  const locale = localeRef.current;
78
75
  const isLoading = isLoadingRef.current;
79
- if (!messages) {
80
- throw new Error("[intor-translator] 'messages' is required");
81
- }
82
76
  const { fallbackLocales, loadingMessage, placeholder, handlers } = translateConfig;
83
77
  const { formatHandler, loadingHandler, missingHandler } = handlers || {};
84
78
  const candidateLocales = resolveCandidateLocales(locale, fallbackLocales);
@@ -102,11 +96,16 @@ var translate = ({
102
96
 
103
97
  // src/translators/base-translator/base-translator.ts
104
98
  var BaseTranslator = class {
99
+ /** Current messages for translation */
100
+ messagesRef;
101
+ /** Current active locale */
102
+ localeRef;
103
+ /** Current loading state */
104
+ isLoadingRef;
105
105
  constructor(options) {
106
- /** Current messages for translation, updatable at runtime */
107
- this.messagesRef = { current: void 0 };
108
- this.messagesRef = { current: options.messages };
106
+ this.messagesRef = { current: options.messages ?? {} };
109
107
  this.localeRef = { current: options.locale };
108
+ this.isLoadingRef = { current: options.isLoading ?? false };
110
109
  }
111
110
  /** Get messages. */
112
111
  get messages() {
@@ -116,6 +115,10 @@ var BaseTranslator = class {
116
115
  get locale() {
117
116
  return this.localeRef.current;
118
117
  }
118
+ /** Get the current loading state. */
119
+ get isLoading() {
120
+ return this.isLoadingRef.current;
121
+ }
119
122
  /**
120
123
  * Replace messages with new ones.
121
124
  *
@@ -133,42 +136,39 @@ var BaseTranslator = class {
133
136
  setLocale(newLocale) {
134
137
  this.localeRef.current = newLocale;
135
138
  }
139
+ /** Set the loading state. */
140
+ setLoading(state) {
141
+ this.isLoadingRef.current = state;
142
+ }
136
143
  };
137
144
 
138
145
  // src/translators/core-translator/core-translator.ts
139
146
  var CoreTranslator = class extends BaseTranslator {
147
+ options;
140
148
  constructor(options) {
141
149
  super({ locale: options.locale, messages: options.messages });
142
- this.isLoadingRef = { current: false };
143
- /** Check if a key exists in the specified locale or current locale. */
144
- this.hasKey = (key, targetLocale) => {
145
- return hasKey({
146
- messagesRef: this.messagesRef,
147
- localeRef: this.localeRef,
148
- key,
149
- targetLocale
150
- });
151
- };
152
- this.t = (key, replacements) => {
153
- return translate({
154
- messagesRef: this.messagesRef,
155
- localeRef: this.localeRef,
156
- isLoadingRef: this.isLoadingRef,
157
- translateConfig: this.options,
158
- key,
159
- replacements
160
- });
161
- };
162
150
  this.options = options;
163
151
  }
164
- /** Get the current loading state. */
165
- get isLoading() {
166
- return this.isLoadingRef.current;
167
- }
168
- /** Set the loading state. */
169
- setLoading(state) {
170
- this.isLoadingRef.current = state;
171
- }
152
+ /** Check if a key exists in the specified locale or current locale. */
153
+ hasKey = (key, targetLocale) => {
154
+ return hasKey({
155
+ messagesRef: this.messagesRef,
156
+ localeRef: this.localeRef,
157
+ key,
158
+ targetLocale
159
+ });
160
+ };
161
+ /** Get the translated message for a key, with optional replacements. */
162
+ t = (key, replacements) => {
163
+ return translate({
164
+ messagesRef: this.messagesRef,
165
+ localeRef: this.localeRef,
166
+ isLoadingRef: this.isLoadingRef,
167
+ translateConfig: this.options,
168
+ key,
169
+ replacements
170
+ });
171
+ };
172
172
  };
173
173
 
174
174
  // src/utils/get-full-key.ts
@@ -183,6 +183,7 @@ var ScopeTranslator = class extends CoreTranslator {
183
183
  constructor(options) {
184
184
  super(options);
185
185
  }
186
+ /** Create a scoped translator with a prefix key, providing `t` and `hasKey` for nested keys. */
186
187
  scoped(preKey) {
187
188
  return {
188
189
  hasKey: (key, targetLocale) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intor-translator",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "A type safe translator that knows what to say and how to handle the rest. Supports custom messages, rich replacements, and async loading.",
5
5
  "author": "Yiming Liao",
6
6
  "license": "MIT",
@@ -60,29 +60,18 @@
60
60
  },
61
61
  "dependencies": {},
62
62
  "devDependencies": {
63
- "@eslint/eslintrc": "^3.3.1",
64
- "@testing-library/dom": "^10.4.0",
65
- "@testing-library/jest-dom": "^6.6.3",
66
- "@testing-library/react": "^16.3.0",
67
63
  "@types/node": "^24.10.1",
68
- "@types/react": "^19.1.4",
69
- "@types/react-dom": "^19.1.5",
70
64
  "@vitest/coverage-v8": "4.0.9",
71
65
  "eslint": "^9.39.1",
72
66
  "eslint-config-prettier": "^10.1.8",
73
67
  "eslint-import-resolver-typescript": "^4.4.4",
74
68
  "eslint-plugin-import": "^2.32.0",
75
- "eslint-plugin-jsx-a11y": "^6.10.2",
76
69
  "eslint-plugin-prettier": "^5.5.4",
77
70
  "eslint-plugin-unicorn": "^62.0.0",
78
71
  "eslint-plugin-unused-imports": "^4.3.0",
79
- "globals": "^16.1.0",
80
72
  "intl-messageformat": "^10.7.16",
81
73
  "knip": "^5.69.1",
82
- "next": "^15.3.2",
83
- "prettier": "^3.5.3",
84
- "react": "^19.1.0",
85
- "react-dom": "^19.1.0",
74
+ "prettier": "^3.6.2",
86
75
  "ts-node": "^10.9.2",
87
76
  "tsd": "^0.33.0",
88
77
  "tsup": "^8.4.0",