@idioma/react 0.1.10 → 0.1.12

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.
@@ -16,10 +16,12 @@ interface BaseIdiomaConfig {
16
16
  MessageValues: Record<string, Record<string, unknown>>;
17
17
  MessageComponents: Record<string, unknown[]>;
18
18
  }
19
- /** Keys in MessageValues that have at least one required value */
20
- type KeysWithValues<MV extends Record<string, Record<string, unknown>>> = { [K in keyof MV]: MV[K] extends Record<string, never> ? never : K }[keyof MV];
21
- /** Keys in MessageValues that have no required values */
22
- type KeysWithoutValues<MV extends Record<string, Record<string, unknown>>> = { [K in keyof MV]: MV[K] extends Record<string, never> ? K : never }[keyof MV];
19
+ /** Keys in MessageValues that have at least one required value.
20
+ * TK filters to specific TranslationKey union, excluding index signature keys. */
21
+ type KeysWithValues<MV extends Record<string, Record<string, unknown>>, TK extends string = Extract<keyof MV, string>> = { [K in TK & keyof MV]: MV[K] extends Record<string, never> ? never : K }[TK & keyof MV];
22
+ /** Keys in MessageValues that have no required values.
23
+ * TK filters to specific TranslationKey union, excluding index signature keys. */
24
+ type KeysWithoutValues<MV extends Record<string, Record<string, unknown>>, TK extends string = Extract<keyof MV, string>> = { [K in TK & keyof MV]: MV[K] extends Record<string, never> ? K : never }[TK & keyof MV];
23
25
  /**
24
26
  * Derive string-only keys from TranslationKey + MessageComponents.
25
27
  * Keys with empty component arrays [] can be used with useT.
@@ -59,24 +61,19 @@ interface SourceTextOptions {
59
61
  * The TFunction only accepts keys where MessageComponents[K] is an empty array (string-only keys).
60
62
  */
61
63
  type TFunction<C extends BaseIdiomaConfig = BaseIdiomaConfig> = {
62
- <K$1 extends StringOnlyKeys<C['TranslationKey'], C['MessageComponents']> & string>(args: K$1 extends KeysWithValues<C['MessageValues']> ? {
64
+ <K$1 extends KeysWithValues<C['MessageValues'], C['TranslationKey']> & StringOnlyKeys<C['TranslationKey'], C['MessageComponents']> & string>(args: {
63
65
  id: K$1;
64
66
  values: C['MessageValues'][K$1];
65
67
  source?: string;
66
68
  context?: string;
67
69
  ns?: string;
68
- } : K$1 extends KeysWithoutValues<C['MessageValues']> ? {
70
+ }): string;
71
+ <K$1 extends KeysWithoutValues<C['MessageValues'], C['TranslationKey']> & StringOnlyKeys<C['TranslationKey'], C['MessageComponents']> & string>(args: {
69
72
  id: K$1;
70
73
  values?: never;
71
74
  source?: string;
72
75
  context?: string;
73
76
  ns?: string;
74
- } : {
75
- id: K$1;
76
- values?: Record<string, unknown>;
77
- source?: string;
78
- context?: string;
79
- ns?: string;
80
77
  }): string;
81
78
  <S extends string>(source: HasPlaceholders<S> extends true ? S : never, values: PlaceholderValues<S>): string;
82
79
  <S extends string>(source: HasPlaceholders<S> extends false ? S : never): string;
@@ -1 +1 @@
1
- {"version":3,"file":"createUseT.d.ts","names":[],"sources":["../src/createUseT.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"createUseT.d.ts","names":[],"sources":["../src/createUseT.ts"],"sourcesContent":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{IdiomaContext as e}from"./context.js";import{interpolateValues as t}from"./interpolate.js";import{useCallback as n,useContext as r}from"react";function i(){return function(){let i=r(e);if(!i)throw Error(`[idioma] useT must be used within an IdiomaProvider. Make sure to wrap your app with <IdiomaProvider>.`);let{locale:a}=i;return n((e,n,r)=>{if(typeof e==`object`){let{id:n,source:r,values:i}=e;console.warn(`[idioma] t({ id: "${n}" }) was not transformed by Babel. Make sure @idioma/core Babel plugin is configured.`);let a=r||n;return i&&Object.keys(i).length>0?t(a,i):a}let i=e,o=n,s,c=n?Object.keys(n):[],l=c[0],u=l&&n?n[l]:void 0;if(c.length===1&&u&&typeof u==`object`&&u[a]!==void 0&&(s=u,o=r),!s)return o&&Object.keys(o).length>0?t(i,o):i;let d=s[a]??Object.values(s)[0];return d===void 0?o&&Object.keys(o).length>0?t(i,o):i:typeof d==`function`?d(o||{}):o&&Object.keys(o).length>0?t(d,o):d},[a])}}export{i as createUseT};
1
+ import{IdiomaContext as e}from"./context.js";import{interpolateValues as t}from"./interpolate.js";import{useCallback as n,useContext as r}from"react";function i(){return function(){let i=r(e);if(!i)throw Error(`[idioma] useT must be used within an IdiomaProvider. Make sure to wrap your app with <IdiomaProvider>.`);let{locale:a}=i;return n((e,n,r)=>{if(typeof e==`object`){let{id:n,source:r,values:i}=e;console.warn(`[idioma] t({ id: "${n}" }) was not transformed by Babel. Make sure @idioma/core Babel plugin is configured.`);let a=r||n;return i&&Object.keys(i).length>0?t(a,i):a}let i=e,o=n,s;if(n&&typeof n==`object`&&`__m`in n&&(s=n.__m,o=r),!s)return o&&Object.keys(o).length>0?t(i,o):i;let c=s[a]??Object.values(s)[0];return c===void 0?o&&Object.keys(o).length>0?t(i,o):i:typeof c==`function`?c(o||{}):o&&Object.keys(o).length>0?t(c,o):c},[a])}}export{i as createUseT};
2
2
  //# sourceMappingURL=createUseT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createUseT.js","names":["useCallback","useContext","IdiomaContext","interpolateValues","createUseT","useT","ctx","Error","locale","sourceOrArgs","values","options","id","source","objValues","console","warn","fallbackText","Object","keys","length","actualValues","localeMessages","valuesKeys","firstKey","firstValue","undefined","msg"],"sources":["../src/createUseT.ts"],"sourcesContent":["import { useCallback, useContext } from 'react';\nimport { IdiomaContext } from './context';\nimport { interpolateValues } from './interpolate';\n\ntype MessageFunction = (args: Record<string, unknown>) => string;\n\n// =============================================================================\n// IdiomaTypes - Single type parameter for cleaner API\n// =============================================================================\n\n/**\n * Base interface for project-specific Idioma types.\n * Generated by the compiler in types.d.ts.\n *\n * @example\n * // In generated types.d.ts:\n * export interface IdiomaTypes {\n * TranslationKey: 'greeting' | 'farewell';\n * MessageValues: { greeting: { name: string } };\n * MessageComponents: { greeting: [] };\n * }\n */\nexport interface BaseIdiomaConfig {\n TranslationKey: string;\n MessageValues: Record<string, Record<string, unknown>>;\n MessageComponents: Record<string, unknown[]>;\n}\n\n// =============================================================================\n// Helper Types for Strict Typing\n// =============================================================================\n\n// --- Key-only mode helpers ---\n\n/** Keys in MessageValues that have at least one required value */\nexport type KeysWithValues<MV extends Record<string, Record<string, unknown>>> =\n {\n [K in keyof MV]: MV[K] extends Record<string, never> ? never : K;\n }[keyof MV];\n\n/** Keys in MessageValues that have no required values */\nexport type KeysWithoutValues<\n MV extends Record<string, Record<string, unknown>>,\n> = {\n [K in keyof MV]: MV[K] extends Record<string, never> ? K : never;\n}[keyof MV];\n\n/**\n * Derive string-only keys from TranslationKey + MessageComponents.\n * Keys with empty component arrays [] can be used with useT.\n * Keys with non-empty component arrays [TransComponent, ...] must use Trans.\n */\nexport type StringOnlyKeys<\n TK extends string,\n MC extends Record<string, unknown[]>,\n> = {\n [K in TK & keyof MC]: MC[K] extends [] ? K : never;\n}[TK & keyof MC];\n\n// --- Source text mode helpers ---\n\n/**\n * Recursively extract placeholder names from a template string.\n * \"Hello {name}, you have {count} items\" → \"name\" | \"count\"\n */\nexport type ExtractPlaceholders<S extends string> =\n S extends `${infer _}{${infer Key}}${infer Rest}`\n ? Key | ExtractPlaceholders<Rest>\n : never;\n\n/**\n * Build the required values object from extracted placeholders.\n * \"Hello {name}\" → { name: string | number }\n * \"No placeholders\" → undefined\n */\nexport type PlaceholderValues<S extends string> =\n ExtractPlaceholders<S> extends never\n ? undefined\n : { [K in ExtractPlaceholders<S>]: string | number };\n\n/** Check if a string has any placeholders */\nexport type HasPlaceholders<S extends string> =\n ExtractPlaceholders<S> extends never ? false : true;\n\n/**\n * Source text mode options: t(\"Submit\", undefined, { context: \"button\", ns: \"common\" })\n */\nexport interface SourceTextOptions {\n /** Translator context (changes key hash) */\n context?: string;\n /** Translator comment (extracted to PO #. comment for translators) */\n comment?: string;\n /** Namespace for scoped lookups (placeholder for future) */\n ns?: string;\n}\n\n/**\n * Type for the t function returned by useT.\n * Supports both source text mode and key-only mode with full type safety.\n *\n * @template C - IdiomaTypes interface containing TranslationKey, MessageValues, MessageComponents\n *\n * The TFunction only accepts keys where MessageComponents[K] is an empty array (string-only keys).\n */\nexport type TFunction<C extends BaseIdiomaConfig = BaseIdiomaConfig> = {\n // === Key-only mode ===\n\n // Keys that require values (only string-only keys allowed)\n <\n K extends StringOnlyKeys<C['TranslationKey'], C['MessageComponents']> &\n string,\n >(\n args: K extends KeysWithValues<C['MessageValues']>\n ? {\n id: K;\n values: C['MessageValues'][K];\n source?: string;\n context?: string;\n ns?: string;\n }\n : K extends KeysWithoutValues<C['MessageValues']>\n ? {\n id: K;\n values?: never;\n source?: string;\n context?: string;\n ns?: string;\n }\n : {\n id: K;\n values?: Record<string, unknown>;\n source?: string;\n context?: string;\n ns?: string;\n },\n ): string;\n\n // === Source text mode ===\n\n // Source text WITH placeholders - values required\n <S extends string>(\n source: HasPlaceholders<S> extends true ? S : never,\n values: PlaceholderValues<S>,\n ): string;\n\n // Source text WITHOUT placeholders - no values\n <S extends string>(\n source: HasPlaceholders<S> extends false ? S : never,\n ): string;\n\n // Source text with options (3rd arg)\n <S extends string>(\n source: S,\n values: PlaceholderValues<S>,\n options: SourceTextOptions,\n ): string;\n};\n\n/**\n * Creates a typed useT hook for imperative translations.\n *\n * Supports both source text mode and key-only mode:\n * - Source text: t('Hello world!') - hashes and looks up\n * - Key-only: t({ id: 'welcome' }) - direct key lookup\n *\n * Only works with messages that don't require component interpolation,\n * since it returns a string. For messages with <0>...</0> tags, use <Trans>.\n *\n * @example\n * // In generated idioma/index.ts:\n * export const useT = createUseT<IdiomaTypes>()\n *\n * // Usage:\n * const t = useT()\n * t('Hello world!') // source text mode\n * t('Hello {name}', { name: 'Ben' }) // with values (2nd arg)\n * t('Submit', undefined, { context: 'button' }) // with options (3rd arg)\n * t('Hello {name}', { name: 'Ben' }, { context: 'greeting' }) // both\n * t({ id: 'welcome' }) // key-only mode\n * t({ id: 'greeting', values: { name: 'Ben' }, ns: 'common' }) // with ns\n */\n// Internal runtime type for key-only args (untyped at runtime)\ninterface KeyOnlyArgsRuntime {\n id: string;\n source?: string;\n values?: Record<string, unknown>;\n context?: string;\n ns?: string;\n}\n\nexport function createUseT<\n C extends BaseIdiomaConfig = BaseIdiomaConfig,\n>(): () => TFunction<C> {\n /**\n * useT hook for imperative translations.\n *\n * Babel transforms t() calls to include inlined translations.\n * This hook handles the transformed calls and provides fallback behavior.\n */\n return function useT(): TFunction<C> {\n const ctx = useContext(IdiomaContext);\n if (!ctx) {\n throw new Error(\n '[idioma] useT must be used within an IdiomaProvider. ' +\n 'Make sure to wrap your app with <IdiomaProvider>.',\n );\n }\n\n const { locale } = ctx;\n\n return useCallback(\n (\n sourceOrArgs: string | KeyOnlyArgsRuntime,\n values?: Record<string, unknown>,\n options?: SourceTextOptions,\n ): string => {\n // Key-only mode: t({ id: 'welcome', source: 'Hello', values: { name }, ns: 'common' })\n // Babel should transform this, but if not, use source as fallback text\n if (typeof sourceOrArgs === 'object') {\n const { id, source, values: objValues } = sourceOrArgs;\n console.warn(\n `[idioma] t({ id: \"${id}\" }) was not transformed by Babel. ` +\n 'Make sure @idioma/core Babel plugin is configured.',\n );\n const fallbackText = source || id;\n if (objValues && Object.keys(objValues).length > 0) {\n return interpolateValues(fallbackText, objValues);\n }\n return fallbackText;\n }\n\n // Source text mode: t('Hello {name}', { name: 'Ben' }, { context: 'button' })\n // OR after Babel transformation: t('Hello {name}', { key: { en: '...', es: '...' } }, { name: 'Ben' })\n const source = sourceOrArgs;\n\n // Detect if Babel has inlined translations in arg 2\n // Babel transforms: t('src', { key: { en: '...', es: '...' } }, { actualValues })\n // The inlined object has ONE key (the message key) whose value has locale keys\n let actualValues = values;\n let localeMessages:\n | Record<string, string | MessageFunction>\n | undefined;\n\n const valuesKeys = values ? Object.keys(values) : [];\n const firstKey = valuesKeys[0];\n const firstValue =\n firstKey && values\n ? (values as Record<string, unknown>)[firstKey]\n : undefined;\n\n // Check if this looks like Babel-inlined translations:\n // - Has exactly one key (the message key)\n // - That key's value is an object with locale as a property\n if (\n valuesKeys.length === 1 &&\n firstValue &&\n typeof firstValue === 'object' &&\n typeof (firstValue as Record<string, unknown>)[locale] !== 'undefined'\n ) {\n // Babel transformed: arg 2 is { messageKey: { en: '...', es: '...' } }\n localeMessages = firstValue as Record<\n string,\n string | MessageFunction\n >;\n actualValues = options as unknown as Record<string, unknown>;\n }\n\n if (!localeMessages) {\n // Not transformed by Babel - fallback to source text\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n const msg = localeMessages[locale] ?? Object.values(localeMessages)[0];\n if (msg === undefined) {\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n if (typeof msg === 'function') {\n return msg(actualValues || {});\n }\n\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(msg, actualValues)\n : msg;\n },\n [locale],\n ) as TFunction<C>;\n };\n}\n"],"mappings":"sJA8LA,SAAgBI,GAEQ,CAOtB,OAAO,UAA8B,CACnC,IAAME,EAAML,EAAWC,EAAc,CACrC,GAAI,CAACI,EACH,MAAUC,MACR,yGAED,CAGH,GAAM,CAAEC,UAAWF,EAEnB,OAAON,GAEHS,EACAC,EACAC,IACW,CAGX,GAAI,OAAOF,GAAiB,SAAU,CACpC,GAAM,CAAEG,KAAIC,OAAAA,EAAQH,OAAQI,GAAcL,EAC1CM,QAAQC,KACN,qBAAqBJ,EAAE,uFAExB,CACD,IAAMK,EAAeJ,GAAUD,EAI/B,OAHIE,GAAaI,OAAOC,KAAKL,EAAU,CAACM,OAAS,EACxCjB,EAAkBc,EAAcH,EAAU,CAE5CG,EAKT,IAAMJ,EAASJ,EAKXY,EAAeX,EACfY,EAIEC,EAAab,EAASQ,OAAOC,KAAKT,EAAO,CAAG,EAAE,CAC9Cc,EAAWD,EAAW,GACtBE,EACJD,GAAYd,EACPA,EAAmCc,GACpCE,IAAAA,GAmBN,GAbEH,EAAWH,SAAW,GACtBK,GACA,OAAOA,GAAe,UACdA,EAAuCjB,KAAY,SAG3Dc,EAAiBG,EAIjBJ,EAAeV,GAGb,CAACW,EAEH,OAAOD,GAAgBH,OAAOC,KAAKE,EAAa,CAACD,OAAS,EACtDjB,EAAkBU,EAAQQ,EAAa,CACvCR,EAGN,IAAMc,EAAML,EAAed,IAAWU,OAAOR,OAAOY,EAAe,CAAC,GAWpE,OAVIK,IAAQD,IAAAA,GACHL,GAAgBH,OAAOC,KAAKE,EAAa,CAACD,OAAS,EACtDjB,EAAkBU,EAAQQ,EAAa,CACvCR,EAGF,OAAOc,GAAQ,WACVA,EAAIN,GAAgB,EAAE,CAAC,CAGzBA,GAAgBH,OAAOC,KAAKE,EAAa,CAACD,OAAS,EACtDjB,EAAkBwB,EAAKN,EAAa,CACpCM,GAEN,CAACnB,EACH,CAAC"}
1
+ {"version":3,"file":"createUseT.js","names":["useCallback","useContext","IdiomaContext","interpolateValues","createUseT","useT","ctx","Error","locale","sourceOrArgs","values","options","id","source","objValues","console","warn","fallbackText","Object","keys","length","actualValues","localeMessages","__m","msg","undefined"],"sources":["../src/createUseT.ts"],"sourcesContent":["import { useCallback, useContext } from 'react';\nimport { IdiomaContext } from './context';\nimport { interpolateValues } from './interpolate';\n\ntype MessageFunction = (args: Record<string, unknown>) => string;\n\n// =============================================================================\n// IdiomaTypes - Single type parameter for cleaner API\n// =============================================================================\n\n/**\n * Base interface for project-specific Idioma types.\n * Generated by the compiler in types.d.ts.\n *\n * @example\n * // In generated types.d.ts:\n * export interface IdiomaTypes {\n * TranslationKey: 'greeting' | 'farewell';\n * MessageValues: { greeting: { name: string } };\n * MessageComponents: { greeting: [] };\n * }\n */\nexport interface BaseIdiomaConfig {\n TranslationKey: string;\n MessageValues: Record<string, Record<string, unknown>>;\n MessageComponents: Record<string, unknown[]>;\n}\n\n// =============================================================================\n// Helper Types for Strict Typing\n// =============================================================================\n\n// --- Key-only mode helpers ---\n\n/** Keys in MessageValues that have at least one required value.\n * TK filters to specific TranslationKey union, excluding index signature keys. */\nexport type KeysWithValues<\n MV extends Record<string, Record<string, unknown>>,\n TK extends string = Extract<keyof MV, string>,\n> = {\n [K in TK & keyof MV]: MV[K] extends Record<string, never> ? never : K;\n}[TK & keyof MV];\n\n/** Keys in MessageValues that have no required values.\n * TK filters to specific TranslationKey union, excluding index signature keys. */\nexport type KeysWithoutValues<\n MV extends Record<string, Record<string, unknown>>,\n TK extends string = Extract<keyof MV, string>,\n> = {\n [K in TK & keyof MV]: MV[K] extends Record<string, never> ? K : never;\n}[TK & keyof MV];\n\n/**\n * Derive string-only keys from TranslationKey + MessageComponents.\n * Keys with empty component arrays [] can be used with useT.\n * Keys with non-empty component arrays [TransComponent, ...] must use Trans.\n */\nexport type StringOnlyKeys<\n TK extends string,\n MC extends Record<string, unknown[]>,\n> = {\n [K in TK & keyof MC]: MC[K] extends [] ? K : never;\n}[TK & keyof MC];\n\n// --- Source text mode helpers ---\n\n/**\n * Recursively extract placeholder names from a template string.\n * \"Hello {name}, you have {count} items\" → \"name\" | \"count\"\n */\nexport type ExtractPlaceholders<S extends string> =\n S extends `${infer _}{${infer Key}}${infer Rest}`\n ? Key | ExtractPlaceholders<Rest>\n : never;\n\n/**\n * Build the required values object from extracted placeholders.\n * \"Hello {name}\" → { name: string | number }\n * \"No placeholders\" → undefined\n */\nexport type PlaceholderValues<S extends string> =\n ExtractPlaceholders<S> extends never\n ? undefined\n : { [K in ExtractPlaceholders<S>]: string | number };\n\n/** Check if a string has any placeholders */\nexport type HasPlaceholders<S extends string> =\n ExtractPlaceholders<S> extends never ? false : true;\n\n/**\n * Source text mode options: t(\"Submit\", undefined, { context: \"button\", ns: \"common\" })\n */\nexport interface SourceTextOptions {\n /** Translator context (changes key hash) */\n context?: string;\n /** Translator comment (extracted to PO #. comment for translators) */\n comment?: string;\n /** Namespace for scoped lookups (placeholder for future) */\n ns?: string;\n}\n\n/**\n * Type for the t function returned by useT.\n * Supports both source text mode and key-only mode with full type safety.\n *\n * @template C - IdiomaTypes interface containing TranslationKey, MessageValues, MessageComponents\n *\n * The TFunction only accepts keys where MessageComponents[K] is an empty array (string-only keys).\n */\nexport type TFunction<C extends BaseIdiomaConfig = BaseIdiomaConfig> = {\n // === Key-only mode ===\n\n // Keys that require values (only string-only keys allowed)\n <\n K extends KeysWithValues<C['MessageValues'], C['TranslationKey']> &\n StringOnlyKeys<C['TranslationKey'], C['MessageComponents']> &\n string,\n >(args: {\n id: K;\n values: C['MessageValues'][K];\n source?: string;\n context?: string;\n ns?: string;\n }): string;\n\n // Keys that don't require values (only string-only keys allowed)\n <\n K extends KeysWithoutValues<C['MessageValues'], C['TranslationKey']> &\n StringOnlyKeys<C['TranslationKey'], C['MessageComponents']> &\n string,\n >(args: {\n id: K;\n values?: never;\n source?: string;\n context?: string;\n ns?: string;\n }): string;\n\n // === Source text mode ===\n\n // Source text WITH placeholders - values required\n <S extends string>(\n source: HasPlaceholders<S> extends true ? S : never,\n values: PlaceholderValues<S>,\n ): string;\n\n // Source text WITHOUT placeholders - no values\n <S extends string>(\n source: HasPlaceholders<S> extends false ? S : never,\n ): string;\n\n // Source text with options (3rd arg)\n <S extends string>(\n source: S,\n values: PlaceholderValues<S>,\n options: SourceTextOptions,\n ): string;\n};\n\n/**\n * Creates a typed useT hook for imperative translations.\n *\n * Supports both source text mode and key-only mode:\n * - Source text: t('Hello world!') - hashes and looks up\n * - Key-only: t({ id: 'welcome' }) - direct key lookup\n *\n * Only works with messages that don't require component interpolation,\n * since it returns a string. For messages with <0>...</0> tags, use <Trans>.\n *\n * @example\n * // In generated idioma/index.ts:\n * export const useT = createUseT<IdiomaTypes>()\n *\n * // Usage:\n * const t = useT()\n * t('Hello world!') // source text mode\n * t('Hello {name}', { name: 'Ben' }) // with values (2nd arg)\n * t('Submit', undefined, { context: 'button' }) // with options (3rd arg)\n * t('Hello {name}', { name: 'Ben' }, { context: 'greeting' }) // both\n * t({ id: 'welcome' }) // key-only mode\n * t({ id: 'greeting', values: { name: 'Ben' }, ns: 'common' }) // with ns\n */\n// Internal runtime type for key-only args (untyped at runtime)\ninterface KeyOnlyArgsRuntime {\n id: string;\n source?: string;\n values?: Record<string, unknown>;\n context?: string;\n ns?: string;\n}\n\nexport function createUseT<\n C extends BaseIdiomaConfig = BaseIdiomaConfig,\n>(): () => TFunction<C> {\n /**\n * useT hook for imperative translations.\n *\n * Babel transforms t() calls to include inlined translations.\n * This hook handles the transformed calls and provides fallback behavior.\n */\n return function useT(): TFunction<C> {\n const ctx = useContext(IdiomaContext);\n if (!ctx) {\n throw new Error(\n '[idioma] useT must be used within an IdiomaProvider. ' +\n 'Make sure to wrap your app with <IdiomaProvider>.',\n );\n }\n\n const { locale } = ctx;\n\n return useCallback(\n (\n sourceOrArgs: string | KeyOnlyArgsRuntime,\n values?: Record<string, unknown>,\n options?: SourceTextOptions,\n ): string => {\n // Key-only mode: t({ id: 'welcome', source: 'Hello', values: { name }, ns: 'common' })\n // Babel should transform this, but if not, use source as fallback text\n if (typeof sourceOrArgs === 'object') {\n const { id, source, values: objValues } = sourceOrArgs;\n console.warn(\n `[idioma] t({ id: \"${id}\" }) was not transformed by Babel. ` +\n 'Make sure @idioma/core Babel plugin is configured.',\n );\n const fallbackText = source || id;\n if (objValues && Object.keys(objValues).length > 0) {\n return interpolateValues(fallbackText, objValues);\n }\n return fallbackText;\n }\n\n // Source text mode: t('Hello {name}', { name: 'Ben' }, { context: 'button' })\n // OR after Babel transformation: t('Hello {name}', { key: { en: '...', es: '...' } }, { name: 'Ben' })\n const source = sourceOrArgs;\n\n // Detect if Babel has inlined translations in arg 2\n // Babel transforms: t('src', { __m: { en: '...', es: '...' } }, { actualValues })\n let actualValues = values;\n let localeMessages:\n | Record<string, string | MessageFunction>\n | undefined;\n\n // Check if Babel has inlined translations via __m marker\n if (values && typeof values === 'object' && '__m' in values) {\n // Babel transformed: arg 2 is { __m: { en: '...', es: '...' } }\n localeMessages = (values as Record<string, unknown>).__m as Record<\n string,\n string | MessageFunction\n >;\n actualValues = options as unknown as Record<string, unknown>;\n }\n\n if (!localeMessages) {\n // Not transformed by Babel - fallback to source text\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n const msg = localeMessages[locale] ?? Object.values(localeMessages)[0];\n if (msg === undefined) {\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n if (typeof msg === 'function') {\n return msg(actualValues || {});\n }\n\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(msg, actualValues)\n : msg;\n },\n [locale],\n ) as TFunction<C>;\n };\n}\n"],"mappings":"sJA+LA,SAAgBI,GAEQ,CAOtB,OAAO,UAA8B,CACnC,IAAME,EAAML,EAAWC,EAAc,CACrC,GAAI,CAACI,EACH,MAAUC,MACR,yGAED,CAGH,GAAM,CAAEC,UAAWF,EAEnB,OAAON,GAEHS,EACAC,EACAC,IACW,CAGX,GAAI,OAAOF,GAAiB,SAAU,CACpC,GAAM,CAAEG,KAAIC,OAAAA,EAAQH,OAAQI,GAAcL,EAC1CM,QAAQC,KACN,qBAAqBJ,EAAE,uFAExB,CACD,IAAMK,EAAeJ,GAAUD,EAI/B,OAHIE,GAAaI,OAAOC,KAAKL,EAAU,CAACM,OAAS,EACxCjB,EAAkBc,EAAcH,EAAU,CAE5CG,EAKT,IAAMJ,EAASJ,EAIXY,EAAeX,EACfY,EAcJ,GATIZ,GAAU,OAAOA,GAAW,UAAY,QAASA,IAEnDY,EAAkBZ,EAAmCa,IAIrDF,EAAeV,GAGb,CAACW,EAEH,OAAOD,GAAgBH,OAAOC,KAAKE,EAAa,CAACD,OAAS,EACtDjB,EAAkBU,EAAQQ,EAAa,CACvCR,EAGN,IAAMW,EAAMF,EAAed,IAAWU,OAAOR,OAAOY,EAAe,CAAC,GAWpE,OAVIE,IAAQC,IAAAA,GACHJ,GAAgBH,OAAOC,KAAKE,EAAa,CAACD,OAAS,EACtDjB,EAAkBU,EAAQQ,EAAa,CACvCR,EAGF,OAAOW,GAAQ,WACVA,EAAIH,GAAgB,EAAE,CAAC,CAGzBA,GAAgBH,OAAOC,KAAKE,EAAa,CAACD,OAAS,EACtDjB,EAAkBqB,EAAKH,EAAa,CACpCG,GAEN,CAAChB,EACH,CAAC"}
package/dist/useT.js CHANGED
@@ -1,2 +1,2 @@
1
- import{IdiomaContext as e}from"./context.js";import{interpolateValues as t}from"./interpolate.js";import{useCallback as n,useContext as r}from"react";function i(i,a){let o=r(e);if(!o)throw Error(`[idioma] useT must be used within an IdiomaProvider. Make sure to wrap your app with <IdiomaProvider>.`);let{locale:s}=o;return n((e,n,r)=>{if(typeof e==`object`){let{id:n,values:r}=e,a=i[n];if(!a)return n;let o=a[s]??Object.values(a)[0];return o===void 0?n:typeof o==`function`?o(r||{}):r?t(o,r):o}let a=e,o=n,c,l=n?Object.keys(n):[],u=l[0],d=u&&n?n[u]:void 0;if(l.length===1&&d&&typeof d==`object`&&d[s]!==void 0)c=d,o=r;else return process.env.NODE_ENV!==`production`&&console.error(`Idioma: Missing translations for "${a}". Ensure the Babel plugin is configured.`),o&&Object.keys(o).length>0?t(a,o):a;if(!c)return o&&Object.keys(o).length>0?t(a,o):a;let f=c[s]??Object.values(c)[0];return f===void 0?o&&Object.keys(o).length>0?t(a,o):a:typeof f==`function`?f(o||{}):o&&Object.keys(o).length>0?t(f,o):f},[i,s])}export{i as __useT};
1
+ import{IdiomaContext as e}from"./context.js";import{interpolateValues as t}from"./interpolate.js";import{useCallback as n,useContext as r}from"react";function i(i,a){let o=r(e);if(!o)throw Error(`[idioma] useT must be used within an IdiomaProvider. Make sure to wrap your app with <IdiomaProvider>.`);let{locale:s}=o;return n((e,n,r)=>{if(typeof e==`object`){let{id:n,values:r}=e,a=i[n];if(!a)return n;let o=a[s]??Object.values(a)[0];return o===void 0?n:typeof o==`function`?o(r||{}):r?t(o,r):o}let a=e,o=n,c;if(n&&typeof n==`object`&&`__m`in n)c=n.__m,o=r;else return process.env.NODE_ENV!==`production`&&console.error(`Idioma: Missing translations for "${a}". Ensure the Babel plugin is configured.`),o&&Object.keys(o).length>0?t(a,o):a;if(!c)return o&&Object.keys(o).length>0?t(a,o):a;let l=c[s]??Object.values(c)[0];return l===void 0?o&&Object.keys(o).length>0?t(a,o):a:typeof l==`function`?l(o||{}):o&&Object.keys(o).length>0?t(l,o):l},[i,s])}export{i as __useT};
2
2
  //# sourceMappingURL=useT.js.map
package/dist/useT.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"useT.js","names":["useCallback","useContext","IdiomaContext","interpolateValues","__useT","translations","_options","context","Error","locale","sourceOrArgs","values","options","id","keyValues","localeMessages","msg","Object","undefined","source","actualValues","valuesKeys","keys","firstKey","firstValue","length","process","env","NODE_ENV","console","error"],"sources":["../src/useT.ts"],"sourcesContent":["import { useCallback, useContext } from 'react';\nimport { IdiomaContext } from './context';\nimport { interpolateValues } from './interpolate';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype MessageFunction = (args: any) => string;\ntype Translations = Record<string, Record<string, string | MessageFunction>>;\n\nexport interface UseTOptions {\n /** Optional namespace */\n ns?: string;\n}\n\n/**\n * Key-only mode arguments: t({ id: \"welcome\", values: { name }, context: \"modal\", ns: \"common\" })\n */\nexport interface KeyOnlyArgs {\n id: string;\n values?: Record<string, unknown>;\n /** Translator context (saved in PO for human reference, not used in key lookup) */\n context?: string;\n /** Namespace for scoped lookups (placeholder for future) */\n ns?: string;\n}\n\n/**\n * Source text mode options: t(\"Submit\", undefined, { context: \"button\", ns: \"common\" })\n */\nexport interface SourceTextOptions {\n /** Translator context (changes key hash) */\n context?: string;\n /** Translator comment (extracted to PO #. comment for translators) */\n comment?: string;\n /** Namespace for scoped lookups (placeholder for future) */\n ns?: string;\n}\n\n/**\n * Type for the t function returned by __useT.\n * Supports both source text mode and key-only mode.\n */\nexport type UseTFunction = {\n (args: KeyOnlyArgs): string;\n (source: string): string;\n (source: string, values: Record<string, unknown>): string;\n (\n source: string,\n values: Record<string, unknown> | undefined,\n options: SourceTextOptions,\n ): string;\n};\n\n/**\n * Internal useT hook used by Babel-compiled output.\n * Not intended for direct use - import useT from the generated idioma/ folder instead.\n *\n * Supports both source text mode and key-only mode:\n * - Source text: t('Hello world!') - hashes and looks up\n * - Key-only: t({ id: 'welcome' }) - direct key lookup\n *\n * @example\n * // Compiled output:\n * const t = __useT(__$idioma)\n * const label = t('Hello world!')\n * const greeting = t('Hello {name}', { name })\n * t('Submit', undefined, { context: 'button' }) // with options (3rd arg)\n * t({ id: 'key', ns: 'common' }) // key-only with namespace\n */\nexport function __useT(\n translations: Translations,\n _options?: UseTOptions,\n): UseTFunction {\n const context = useContext(IdiomaContext);\n if (!context) {\n throw new Error(\n '[idioma] useT must be used within an IdiomaProvider. ' +\n 'Make sure to wrap your app with <IdiomaProvider>.',\n );\n }\n\n const { locale } = context;\n\n return useCallback(\n (\n sourceOrArgs: string | KeyOnlyArgs,\n values?: Record<string, unknown>,\n options?: SourceTextOptions,\n ): string => {\n // Key-only mode: t({ id: 'welcome', values: { name }, ns: 'common' })\n if (typeof sourceOrArgs === 'object') {\n const { id, values: keyValues } = sourceOrArgs;\n const localeMessages = translations[id];\n if (!localeMessages) return id;\n\n const msg = localeMessages[locale] ?? Object.values(localeMessages)[0];\n if (msg === undefined) return id;\n if (typeof msg === 'function') return msg(keyValues || {});\n return keyValues ? interpolateValues(msg, keyValues) : msg;\n }\n\n // Source text mode: t('Hello {name}', { name: 'Ben' }, { context: 'button' })\n // OR after Babel transformation: t('Hello {name}', { en: '...', es: '...' }, { name: 'Ben' })\n const source = sourceOrArgs;\n\n // Detect if Babel has inlined translations in arg 2\n // Babel transforms: t('src', { key: { en: '...', es: '...' } }, { actualValues })\n // The inlined object has ONE key (the message key) whose value has locale keys\n let actualValues = values;\n let localeMessages: Record<string, string | MessageFunction> | undefined;\n\n const valuesKeys = values ? Object.keys(values) : [];\n const firstKey = valuesKeys[0];\n const firstValue =\n firstKey && values\n ? (values as Record<string, unknown>)[firstKey]\n : undefined;\n\n // Check if this looks like Babel-inlined translations:\n // - Has exactly one key (the message key)\n // - That key's value is an object with locale as a property\n if (\n valuesKeys.length === 1 &&\n firstValue &&\n typeof firstValue === 'object' &&\n typeof (firstValue as Record<string, unknown>)[locale] !== 'undefined'\n ) {\n // Babel transformed: arg 2 is { messageKey: { en: '...', es: '...' } }\n localeMessages = firstValue as Record<string, string | MessageFunction>;\n actualValues = options as unknown as Record<string, unknown>;\n } else {\n // Babel didn't inline translations - graceful fallback\n if (process.env.NODE_ENV !== 'production') {\n console.error(\n `Idioma: Missing translations for \"${source}\". ` +\n 'Ensure the Babel plugin is configured.',\n );\n }\n // Return source text (with interpolation if values provided)\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n if (!localeMessages) {\n // Fallback: interpolate source text if values provided\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n const msg = localeMessages[locale] ?? Object.values(localeMessages)[0];\n if (msg === undefined) {\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n if (typeof msg === 'function') {\n return msg(actualValues || {});\n }\n\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(msg, actualValues)\n : msg;\n },\n [translations, locale],\n ) as UseTFunction;\n}\n"],"mappings":"sJAoEA,SAAgBI,EACdC,EACAC,EACc,CACd,IAAMC,EAAUN,EAAWC,EAAc,CACzC,GAAI,CAACK,EACH,MAAUC,MACR,yGAED,CAGH,GAAM,CAAEC,UAAWF,EAEnB,OAAOP,GAEHU,EACAC,EACAC,IACW,CAEX,GAAI,OAAOF,GAAiB,SAAU,CACpC,GAAM,CAAEG,KAAIF,OAAQG,GAAcJ,EAC5BK,EAAiBV,EAAaQ,GACpC,GAAI,CAACE,EAAgB,OAAOF,EAE5B,IAAMG,EAAMD,EAAeN,IAAWQ,OAAON,OAAOI,EAAe,CAAC,GAGpE,OAFIC,IAAQE,IAAAA,GAAkBL,EAC1B,OAAOG,GAAQ,WAAmBA,EAAIF,GAAa,EAAE,CAAC,CACnDA,EAAYX,EAAkBa,EAAKF,EAAU,CAAGE,EAKzD,IAAMG,EAAST,EAKXU,EAAeT,EACfI,EAEEM,EAAaV,EAASM,OAAOK,KAAKX,EAAO,CAAG,EAAE,CAC9CY,EAAWF,EAAW,GACtBG,EACJD,GAAYZ,EACPA,EAAmCY,GACpCL,IAAAA,GAKN,GACEG,EAAWI,SAAW,GACtBD,GACA,OAAOA,GAAe,UACdA,EAAuCf,KAAY,OAG3DM,EAAiBS,EACjBJ,EAAeR,OAUf,OAPIc,QAAQC,IAAIC,WAAa,cAC3BC,QAAQC,MACN,qCAAqCX,EAAM,2CAE5C,CAGIC,GAAgBH,OAAOK,KAAKF,EAAa,CAACK,OAAS,EACtDtB,EAAkBgB,EAAQC,EAAa,CACvCD,EAGN,GAAI,CAACJ,EAEH,OAAOK,GAAgBH,OAAOK,KAAKF,EAAa,CAACK,OAAS,EACtDtB,EAAkBgB,EAAQC,EAAa,CACvCD,EAGN,IAAMH,EAAMD,EAAeN,IAAWQ,OAAON,OAAOI,EAAe,CAAC,GAWpE,OAVIC,IAAQE,IAAAA,GACHE,GAAgBH,OAAOK,KAAKF,EAAa,CAACK,OAAS,EACtDtB,EAAkBgB,EAAQC,EAAa,CACvCD,EAGF,OAAOH,GAAQ,WACVA,EAAII,GAAgB,EAAE,CAAC,CAGzBA,GAAgBH,OAAOK,KAAKF,EAAa,CAACK,OAAS,EACtDtB,EAAkBa,EAAKI,EAAa,CACpCJ,GAEN,CAACX,EAAcI,EACjB,CAAC"}
1
+ {"version":3,"file":"useT.js","names":["useCallback","useContext","IdiomaContext","interpolateValues","__useT","translations","_options","context","Error","locale","sourceOrArgs","values","options","id","keyValues","localeMessages","msg","Object","undefined","source","actualValues","__m","process","env","NODE_ENV","console","error","keys","length"],"sources":["../src/useT.ts"],"sourcesContent":["import { useCallback, useContext } from 'react';\nimport { IdiomaContext } from './context';\nimport { interpolateValues } from './interpolate';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype MessageFunction = (args: any) => string;\ntype Translations = Record<string, Record<string, string | MessageFunction>>;\n\nexport interface UseTOptions {\n /** Optional namespace */\n ns?: string;\n}\n\n/**\n * Key-only mode arguments: t({ id: \"welcome\", values: { name }, context: \"modal\", ns: \"common\" })\n */\nexport interface KeyOnlyArgs {\n id: string;\n values?: Record<string, unknown>;\n /** Translator context (saved in PO for human reference, not used in key lookup) */\n context?: string;\n /** Namespace for scoped lookups (placeholder for future) */\n ns?: string;\n}\n\n/**\n * Source text mode options: t(\"Submit\", undefined, { context: \"button\", ns: \"common\" })\n */\nexport interface SourceTextOptions {\n /** Translator context (changes key hash) */\n context?: string;\n /** Translator comment (extracted to PO #. comment for translators) */\n comment?: string;\n /** Namespace for scoped lookups (placeholder for future) */\n ns?: string;\n}\n\n/**\n * Type for the t function returned by __useT.\n * Supports both source text mode and key-only mode.\n */\nexport type UseTFunction = {\n (args: KeyOnlyArgs): string;\n (source: string): string;\n (source: string, values: Record<string, unknown>): string;\n (\n source: string,\n values: Record<string, unknown> | undefined,\n options: SourceTextOptions,\n ): string;\n};\n\n/**\n * Internal useT hook used by Babel-compiled output.\n * Not intended for direct use - import useT from the generated idioma/ folder instead.\n *\n * Supports both source text mode and key-only mode:\n * - Source text: t('Hello world!') - hashes and looks up\n * - Key-only: t({ id: 'welcome' }) - direct key lookup\n *\n * @example\n * // Compiled output:\n * const t = __useT(__$idioma)\n * const label = t('Hello world!')\n * const greeting = t('Hello {name}', { name })\n * t('Submit', undefined, { context: 'button' }) // with options (3rd arg)\n * t({ id: 'key', ns: 'common' }) // key-only with namespace\n */\nexport function __useT(\n translations: Translations,\n _options?: UseTOptions,\n): UseTFunction {\n const context = useContext(IdiomaContext);\n if (!context) {\n throw new Error(\n '[idioma] useT must be used within an IdiomaProvider. ' +\n 'Make sure to wrap your app with <IdiomaProvider>.',\n );\n }\n\n const { locale } = context;\n\n return useCallback(\n (\n sourceOrArgs: string | KeyOnlyArgs,\n values?: Record<string, unknown>,\n options?: SourceTextOptions,\n ): string => {\n // Key-only mode: t({ id: 'welcome', values: { name }, ns: 'common' })\n if (typeof sourceOrArgs === 'object') {\n const { id, values: keyValues } = sourceOrArgs;\n const localeMessages = translations[id];\n if (!localeMessages) return id;\n\n const msg = localeMessages[locale] ?? Object.values(localeMessages)[0];\n if (msg === undefined) return id;\n if (typeof msg === 'function') return msg(keyValues || {});\n return keyValues ? interpolateValues(msg, keyValues) : msg;\n }\n\n // Source text mode: t('Hello {name}', { name: 'Ben' }, { context: 'button' })\n // OR after Babel transformation: t('Hello {name}', { __m: { en: '...', es: '...' } }, { name: 'Ben' })\n const source = sourceOrArgs;\n\n // Detect if Babel has inlined translations via __m marker\n let actualValues = values;\n let localeMessages: Record<string, string | MessageFunction> | undefined;\n\n if (values && typeof values === 'object' && '__m' in values) {\n // Babel transformed: arg 2 is { __m: { en: '...', es: '...' } }\n localeMessages = (values as Record<string, unknown>).__m as Record<\n string,\n string | MessageFunction\n >;\n actualValues = options as unknown as Record<string, unknown>;\n } else {\n // Babel didn't inline translations - graceful fallback\n if (process.env.NODE_ENV !== 'production') {\n console.error(\n `Idioma: Missing translations for \"${source}\". ` +\n 'Ensure the Babel plugin is configured.',\n );\n }\n // Return source text (with interpolation if values provided)\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n if (!localeMessages) {\n // Fallback: interpolate source text if values provided\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n const msg = localeMessages[locale] ?? Object.values(localeMessages)[0];\n if (msg === undefined) {\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(source, actualValues)\n : source;\n }\n\n if (typeof msg === 'function') {\n return msg(actualValues || {});\n }\n\n return actualValues && Object.keys(actualValues).length > 0\n ? interpolateValues(msg, actualValues)\n : msg;\n },\n [translations, locale],\n ) as UseTFunction;\n}\n"],"mappings":"sJAoEA,SAAgBI,EACdC,EACAC,EACc,CACd,IAAMC,EAAUN,EAAWC,EAAc,CACzC,GAAI,CAACK,EACH,MAAUC,MACR,yGAED,CAGH,GAAM,CAAEC,UAAWF,EAEnB,OAAOP,GAEHU,EACAC,EACAC,IACW,CAEX,GAAI,OAAOF,GAAiB,SAAU,CACpC,GAAM,CAAEG,KAAIF,OAAQG,GAAcJ,EAC5BK,EAAiBV,EAAaQ,GACpC,GAAI,CAACE,EAAgB,OAAOF,EAE5B,IAAMG,EAAMD,EAAeN,IAAWQ,OAAON,OAAOI,EAAe,CAAC,GAGpE,OAFIC,IAAQE,IAAAA,GAAkBL,EAC1B,OAAOG,GAAQ,WAAmBA,EAAIF,GAAa,EAAE,CAAC,CACnDA,EAAYX,EAAkBa,EAAKF,EAAU,CAAGE,EAKzD,IAAMG,EAAST,EAGXU,EAAeT,EACfI,EAEJ,GAAIJ,GAAU,OAAOA,GAAW,UAAY,QAASA,EAEnDI,EAAkBJ,EAAmCU,IAIrDD,EAAeR,OAUf,OAPIU,QAAQC,IAAIC,WAAa,cAC3BC,QAAQC,MACN,qCAAqCP,EAAM,2CAE5C,CAGIC,GAAgBH,OAAOU,KAAKP,EAAa,CAACQ,OAAS,EACtDzB,EAAkBgB,EAAQC,EAAa,CACvCD,EAGN,GAAI,CAACJ,EAEH,OAAOK,GAAgBH,OAAOU,KAAKP,EAAa,CAACQ,OAAS,EACtDzB,EAAkBgB,EAAQC,EAAa,CACvCD,EAGN,IAAMH,EAAMD,EAAeN,IAAWQ,OAAON,OAAOI,EAAe,CAAC,GAWpE,OAVIC,IAAQE,IAAAA,GACHE,GAAgBH,OAAOU,KAAKP,EAAa,CAACQ,OAAS,EACtDzB,EAAkBgB,EAAQC,EAAa,CACvCD,EAGF,OAAOH,GAAQ,WACVA,EAAII,GAAgB,EAAE,CAAC,CAGzBA,GAAgBH,OAAOU,KAAKP,EAAa,CAACQ,OAAS,EACtDzB,EAAkBa,EAAKI,EAAa,CACpCJ,GAEN,CAACX,EAAcI,EACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idioma/react",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Compile-time React i18n - runtime package",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "license": "MIT",
51
51
  "dependencies": {
52
- "@idioma/core": "0.1.10"
52
+ "@idioma/core": "0.1.12"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsdown",