@intlayer/react-i18next 9.0.0-canary.11 → 9.0.0-canary.13

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.
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useTranslation } from "./useTranslation.mjs";
3
+ import { useTranslationLoose } from "./useTranslation.mjs";
4
4
  import { parseTaggedMessage } from "@intlayer/core/messageFormat";
5
5
  import { Children, Fragment, cloneElement, isValidElement } from "react";
6
6
  import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
@@ -40,7 +40,7 @@ const renderTokens = (tokens, components, childrenArray) => tokens.map((token, t
40
40
  * ```
41
41
  */
42
42
  const Trans = function Trans({ i18nKey, ns, values, count, context, defaults, components, tOptions, children }) {
43
- const { t } = useTranslation(Array.isArray(ns) ? ns[0] : ns);
43
+ const { t } = useTranslationLoose(Array.isArray(ns) ? ns[0] : ns);
44
44
  const translateOptions = {
45
45
  ...tOptions ?? {},
46
46
  ...values ?? {}
@@ -1 +1 @@
1
- {"version":3,"file":"Trans.mjs","names":["translateFunction"],"sources":["../../src/Trans.tsx"],"sourcesContent":["'use client';\n\nimport {\n parseTaggedMessage,\n type TaggedMessageToken,\n} from '@intlayer/core/messageFormat';\nimport type { DictionaryKeys } from '@intlayer/types/module_augmentation';\nimport type { Namespace, TOptions } from 'i18next';\nimport {\n Children,\n cloneElement,\n Fragment,\n isValidElement,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { Trans as _Trans, TransProps } from 'react-i18next';\nimport { useTranslation } from './useTranslation';\n\n/**\n * Renders parsed message tokens to React nodes, mapping tags to the\n * `components` prop (object or array form) or to the JSX children\n * (numbered tags `<1>…</1>` index into the children, react-i18next style).\n */\nconst renderTokens = (\n tokens: TaggedMessageToken[],\n components: Record<string, ReactElement> | readonly ReactElement[],\n childrenArray: ReactNode[]\n): ReactNode[] =>\n tokens.map((token, tokenIndex) => {\n if (typeof token === 'string') return token;\n\n const renderedChildren = renderTokens(\n token.children,\n components,\n childrenArray\n );\n\n const mappedComponent = Array.isArray(components)\n ? components[Number(token.tag)]\n : (components as Record<string, ReactElement>)[token.tag];\n\n const fallbackChild = /^\\d+$/.test(token.tag)\n ? childrenArray[Number(token.tag)]\n : undefined;\n\n const elementToClone = mappedComponent ?? fallbackChild;\n\n if (isValidElement(elementToClone)) {\n return cloneElement(\n elementToClone as ReactElement<{ children?: ReactNode }>,\n { key: tokenIndex },\n ...(renderedChildren.length\n ? renderedChildren\n : [\n (elementToClone as ReactElement<{ children?: ReactNode }>).props\n .children,\n ])\n );\n }\n\n // Native HTML tag in the message without a mapping — render it as-is\n if (/^[a-z][\\w-]*$/.test(token.tag)) {\n const NativeTag = token.tag as keyof React.JSX.IntrinsicElements;\n return <NativeTag key={tokenIndex}>{renderedChildren}</NativeTag>;\n }\n\n // Unknown numbered tag — render its children unwrapped\n return <Fragment key={tokenIndex}>{renderedChildren}</Fragment>;\n });\n\n/**\n * Drop-in for react-i18next's `<Trans>`.\n *\n * Supports `values` interpolation, `count` plural resolution, `context`,\n * `defaults` fallback, and component interpolation through the `components`\n * prop (object or array form) or numbered tags mapped onto JSX children.\n *\n * @example\n * ```tsx\n * <Trans i18nKey=\"richText\" components={{ bold: <strong /> }} />\n * <Trans i18nKey=\"hello\" count={2} values={{ name: 'John' }}>\n * Hello <b>{'{{name}}'}</b>\n * </Trans>\n * ```\n */\nexport const Trans: typeof _Trans = function Trans<\n Key extends string = string,\n Ns extends Namespace = Namespace,\n>({\n i18nKey,\n ns,\n values,\n count,\n context,\n defaults,\n components,\n tOptions,\n children,\n}: TransProps<Key, Ns>): React.ReactElement {\n const { t } = useTranslation(\n (Array.isArray(ns) ? ns[0] : ns) as DictionaryKeys\n );\n\n const translateOptions: TOptions = {\n ...((tOptions ?? {}) as TOptions),\n ...((values ?? {}) as Record<string, unknown>),\n };\n if (count !== undefined) translateOptions.count = count;\n if (context !== undefined) translateOptions.context = context;\n if (typeof defaults === 'string') translateOptions.defaultValue = defaults;\n\n const translateFunction = t as unknown as (\n key: string,\n options?: TOptions\n ) => string;\n\n const message = i18nKey\n ? translateFunction(i18nKey as string, translateOptions)\n : (defaults ?? '');\n\n const resolvedMessage =\n typeof message === 'string' && message !== i18nKey\n ? message\n : (defaults ?? message);\n\n if (typeof resolvedMessage !== 'string') {\n return <>{children ?? resolvedMessage}</>;\n }\n\n const childrenArray = Children.toArray(children);\n\n const tokens = parseTaggedMessage(resolvedMessage);\n const hasTags = tokens.some((token) => typeof token !== 'string');\n\n if (!hasTags && !components) {\n return <>{resolvedMessage}</>;\n }\n\n return (\n <>\n {renderTokens(\n tokens,\n (components ?? {}) as Record<string, ReactElement>,\n childrenArray\n )}\n </>\n );\n} as typeof _Trans;\n"],"mappings":";;;;;;;;;;;;;AAwBA,MAAM,gBACJ,QACA,YACA,kBAEA,OAAO,KAAK,OAAO,eAAe;AAChC,KAAI,OAAO,UAAU,SAAU,QAAO;CAEtC,MAAM,mBAAmB,aACvB,MAAM,UACN,YACA,cACD;CAED,MAAM,kBAAkB,MAAM,QAAQ,WAAW,GAC7C,WAAW,OAAO,MAAM,IAAI,IAC3B,WAA4C,MAAM;CAEvD,MAAM,gBAAgB,QAAQ,KAAK,MAAM,IAAI,GACzC,cAAc,OAAO,MAAM,IAAI,IAC/B;CAEJ,MAAM,iBAAiB,mBAAmB;AAE1C,KAAI,eAAe,eAAe,CAChC,QAAO,aACL,gBACA,EAAE,KAAK,YAAY,EACnB,GAAI,iBAAiB,SACjB,mBACA,CACG,eAA0D,MACxD,SACJ,CACN;AAIH,KAAI,gBAAgB,KAAK,MAAM,IAAI,EAAE;EACnC,MAAM,YAAY,MAAM;AACxB,SAAO,oBAAC,WAAD,YAA6B,kBAA6B,EAA1C,WAA0C;;AAInE,QAAO,oBAAC,UAAD,YAA4B,kBAA4B,EAAzC,WAAyC;EAC/D;;;;;;;;;;;;;;;;AAiBJ,MAAa,QAAuB,SAAS,MAG3C,EACA,SACA,IACA,QACA,OACA,SACA,UACA,YACA,UACA,YAC0C;CAC1C,MAAM,EAAE,MAAM,eACX,MAAM,QAAQ,GAAG,GAAG,GAAG,KAAK,GAC9B;CAED,MAAM,mBAA6B;EACjC,GAAK,YAAY,EAAE;EACnB,GAAK,UAAU,EAAE;EAClB;AACD,KAAI,UAAU,OAAW,kBAAiB,QAAQ;AAClD,KAAI,YAAY,OAAW,kBAAiB,UAAU;AACtD,KAAI,OAAO,aAAa,SAAU,kBAAiB,eAAe;CAOlE,MAAM,UAAU,UACZA,EAAkB,SAAmB,iBAAiB,GACrD,YAAY;CAEjB,MAAM,kBACJ,OAAO,YAAY,YAAY,YAAY,UACvC,UACC,YAAY;AAEnB,KAAI,OAAO,oBAAoB,SAC7B,QAAO,4CAAG,YAAY,iBAAmB;CAG3C,MAAM,gBAAgB,SAAS,QAAQ,SAAS;CAEhD,MAAM,SAAS,mBAAmB,gBAAgB;AAGlD,KAAI,CAFY,OAAO,MAAM,UAAU,OAAO,UAAU,SAE5C,IAAI,CAAC,WACf,QAAO,4CAAG,iBAAmB;AAG/B,QACE,4CACG,aACC,QACC,cAAc,EAAE,EACjB,cACD,EACA"}
1
+ {"version":3,"file":"Trans.mjs","names":[],"sources":["../../src/Trans.tsx"],"sourcesContent":["'use client';\n\nimport {\n parseTaggedMessage,\n type TaggedMessageToken,\n} from '@intlayer/core/messageFormat';\nimport type { ValidDotPathsFor } from '@intlayer/core/transpiler';\nimport type { DictionaryKeys } from '@intlayer/types/module_augmentation';\nimport type { Namespace, TOptions } from 'i18next';\nimport {\n Children,\n cloneElement,\n Fragment,\n isValidElement,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { TransProps } from 'react-i18next';\nimport { useTranslationLoose } from './useTranslation';\n\n/**\n * `<Trans>` props for a known dictionary namespace: `i18nKey` is validated\n * against the dictionary's dot-paths, matching the strength of `useIntlayer`.\n */\ntype TypedTransProps<N extends DictionaryKeys, P extends string> = Omit<\n TransProps<string>,\n 'i18nKey' | 'ns'\n> & {\n /** Dictionary namespace the `i18nKey` is validated against. */\n ns: N | readonly N[];\n /** Typed dot-path key into the `ns` dictionary. */\n i18nKey: P;\n};\n\n/**\n * Overload set for {@link Trans}: with a `ns` prop the `i18nKey` is a typed\n * dot-path of that dictionary; without one the key falls back to the default\n * namespace and stays loosely typed.\n */\ntype TypedTrans = {\n <N extends DictionaryKeys, P extends ValidDotPathsFor<N>>(\n props: TypedTransProps<N, P>\n ): ReactElement;\n (props: TransProps<string> & { ns?: undefined }): ReactElement;\n};\n\n/**\n * Renders parsed message tokens to React nodes, mapping tags to the\n * `components` prop (object or array form) or to the JSX children\n * (numbered tags `<1>…</1>` index into the children, react-i18next style).\n */\nconst renderTokens = (\n tokens: TaggedMessageToken[],\n components: Record<string, ReactElement> | readonly ReactElement[],\n childrenArray: ReactNode[]\n): ReactNode[] =>\n tokens.map((token, tokenIndex) => {\n if (typeof token === 'string') return token;\n\n const renderedChildren = renderTokens(\n token.children,\n components,\n childrenArray\n );\n\n const mappedComponent = Array.isArray(components)\n ? components[Number(token.tag)]\n : (components as Record<string, ReactElement>)[token.tag];\n\n const fallbackChild = /^\\d+$/.test(token.tag)\n ? childrenArray[Number(token.tag)]\n : undefined;\n\n const elementToClone = mappedComponent ?? fallbackChild;\n\n if (isValidElement(elementToClone)) {\n return cloneElement(\n elementToClone as ReactElement<{ children?: ReactNode }>,\n { key: tokenIndex },\n ...(renderedChildren.length\n ? renderedChildren\n : [\n (elementToClone as ReactElement<{ children?: ReactNode }>).props\n .children,\n ])\n );\n }\n\n // Native HTML tag in the message without a mapping — render it as-is\n if (/^[a-z][\\w-]*$/.test(token.tag)) {\n const NativeTag = token.tag as keyof React.JSX.IntrinsicElements;\n return <NativeTag key={tokenIndex}>{renderedChildren}</NativeTag>;\n }\n\n // Unknown numbered tag — render its children unwrapped\n return <Fragment key={tokenIndex}>{renderedChildren}</Fragment>;\n });\n\n/**\n * Drop-in for react-i18next's `<Trans>`.\n *\n * Supports `values` interpolation, `count` plural resolution, `context`,\n * `defaults` fallback, and component interpolation through the `components`\n * prop (object or array form) or numbered tags mapped onto JSX children.\n *\n * @example\n * ```tsx\n * <Trans i18nKey=\"richText\" components={{ bold: <strong /> }} />\n * <Trans i18nKey=\"hello\" count={2} values={{ name: 'John' }}>\n * Hello <b>{'{{name}}'}</b>\n * </Trans>\n * ```\n */\nexport const Trans = function Trans<\n Key extends string = string,\n Ns extends Namespace = Namespace,\n>({\n i18nKey,\n ns,\n values,\n count,\n context,\n defaults,\n components,\n tOptions,\n children,\n}: TransProps<Key, Ns>): React.ReactElement {\n const { t } = useTranslationLoose(Array.isArray(ns) ? ns[0] : ns);\n\n const translateOptions: TOptions = {\n ...((tOptions ?? {}) as TOptions),\n ...((values ?? {}) as Record<string, unknown>),\n };\n if (count !== undefined) translateOptions.count = count;\n if (context !== undefined) translateOptions.context = context;\n if (typeof defaults === 'string') translateOptions.defaultValue = defaults;\n\n const message = i18nKey\n ? t(i18nKey as string, translateOptions)\n : (defaults ?? '');\n\n const resolvedMessage =\n typeof message === 'string' && message !== i18nKey\n ? message\n : (defaults ?? message);\n\n if (typeof resolvedMessage !== 'string') {\n return <>{children ?? resolvedMessage}</>;\n }\n\n const childrenArray = Children.toArray(children);\n\n const tokens = parseTaggedMessage(resolvedMessage);\n const hasTags = tokens.some((token) => typeof token !== 'string');\n\n if (!hasTags && !components) {\n return <>{resolvedMessage}</>;\n }\n\n return (\n <>\n {renderTokens(\n tokens,\n (components ?? {}) as Record<string, ReactElement>,\n childrenArray\n )}\n </>\n );\n} as TypedTrans;\n"],"mappings":";;;;;;;;;;;;;AAmDA,MAAM,gBACJ,QACA,YACA,kBAEA,OAAO,KAAK,OAAO,eAAe;AAChC,KAAI,OAAO,UAAU,SAAU,QAAO;CAEtC,MAAM,mBAAmB,aACvB,MAAM,UACN,YACA,cACD;CAED,MAAM,kBAAkB,MAAM,QAAQ,WAAW,GAC7C,WAAW,OAAO,MAAM,IAAI,IAC3B,WAA4C,MAAM;CAEvD,MAAM,gBAAgB,QAAQ,KAAK,MAAM,IAAI,GACzC,cAAc,OAAO,MAAM,IAAI,IAC/B;CAEJ,MAAM,iBAAiB,mBAAmB;AAE1C,KAAI,eAAe,eAAe,CAChC,QAAO,aACL,gBACA,EAAE,KAAK,YAAY,EACnB,GAAI,iBAAiB,SACjB,mBACA,CACG,eAA0D,MACxD,SACJ,CACN;AAIH,KAAI,gBAAgB,KAAK,MAAM,IAAI,EAAE;EACnC,MAAM,YAAY,MAAM;AACxB,SAAO,oBAAC,WAAD,YAA6B,kBAA6B,EAA1C,WAA0C;;AAInE,QAAO,oBAAC,UAAD,YAA4B,kBAA4B,EAAzC,WAAyC;EAC/D;;;;;;;;;;;;;;;;AAiBJ,MAAa,QAAQ,SAAS,MAG5B,EACA,SACA,IACA,QACA,OACA,SACA,UACA,YACA,UACA,YAC0C;CAC1C,MAAM,EAAE,MAAM,oBAAoB,MAAM,QAAQ,GAAG,GAAG,GAAG,KAAK,GAAG;CAEjE,MAAM,mBAA6B;EACjC,GAAK,YAAY,EAAE;EACnB,GAAK,UAAU,EAAE;EAClB;AACD,KAAI,UAAU,OAAW,kBAAiB,QAAQ;AAClD,KAAI,YAAY,OAAW,kBAAiB,UAAU;AACtD,KAAI,OAAO,aAAa,SAAU,kBAAiB,eAAe;CAElE,MAAM,UAAU,UACZ,EAAE,SAAmB,iBAAiB,GACrC,YAAY;CAEjB,MAAM,kBACJ,OAAO,YAAY,YAAY,YAAY,UACvC,UACC,YAAY;AAEnB,KAAI,OAAO,oBAAoB,SAC7B,QAAO,4CAAG,YAAY,iBAAmB;CAG3C,MAAM,gBAAgB,SAAS,QAAQ,SAAS;CAEhD,MAAM,SAAS,mBAAmB,gBAAgB;AAGlD,KAAI,CAFY,OAAO,MAAM,UAAU,OAAO,UAAU,SAE5C,IAAI,CAAC,WACf,QAAO,4CAAG,iBAAmB;AAG/B,QACE,4CACG,aACC,QACC,cAAc,EAAE,EACjB,cACD,EACA"}
@@ -0,0 +1,76 @@
1
+ import { getHTMLTextDir } from "@intlayer/core/localization";
2
+ import { resolveMessage } from "@intlayer/core/messageFormat";
3
+ import { getInterpolationValues, resolveTranslation } from "@intlayer/i18next";
4
+
5
+ //#region src/createTranslationApi.ts
6
+ /**
7
+ * Builds the `{ t, i18n }` pair shared by `useTranslation` (runtime
8
+ * registry lookup) and `useDictionary` / `useDictionaryDynamic`
9
+ * (build-time-imported dictionary content).
10
+ *
11
+ * Translation lookup goes through the shared i18next-dialect resolver:
12
+ * namespace prefixes (`ns:key`), the `ns` option, plural suffixes
13
+ * (`key_one`/`key_other` via `Intl.PluralRules`), context suffixes
14
+ * (`key_male`), `$t()` nesting, `defaultValue` and `{{var}}` interpolation
15
+ * are all supported.
16
+ */
17
+ const createTranslationApi = ({ locale, setLocale, availableLocales, namespace, keyPrefix, dictionaryContent }) => {
18
+ const resolveSingleKey = (key, translateOptions) => resolveTranslation({
19
+ locale,
20
+ namespace,
21
+ key: keyPrefix ? `${keyPrefix}.${key}` : key,
22
+ options: translateOptions,
23
+ dictionaryContent
24
+ });
25
+ const translate = (key, optionsOrDefaultValue, extraOptions) => {
26
+ const translateOptions = typeof optionsOrDefaultValue === "string" ? {
27
+ defaultValue: optionsOrDefaultValue,
28
+ ...extraOptions
29
+ } : optionsOrDefaultValue ?? {};
30
+ const keys = Array.isArray(key) ? key : [key];
31
+ for (const candidateKey of keys) {
32
+ const resolved = resolveSingleKey(candidateKey, translateOptions);
33
+ if (resolved !== void 0) return resolved;
34
+ }
35
+ const defaultValue = translateOptions.defaultValue;
36
+ if (typeof defaultValue === "string") return resolveMessage(defaultValue, getInterpolationValues(translateOptions), locale, "i18next");
37
+ return keys[keys.length - 1];
38
+ };
39
+ return {
40
+ translate,
41
+ i18n: {
42
+ language: locale,
43
+ languages: availableLocales ?? [],
44
+ resolvedLanguage: locale,
45
+ isInitialized: true,
46
+ changeLanguage: async (newLanguage) => {
47
+ setLocale(newLanguage);
48
+ },
49
+ dir: (language) => {
50
+ return getHTMLTextDir(language ?? locale) === "rtl" ? "rtl" : "ltr";
51
+ },
52
+ exists: (key, existsOptions) => resolveTranslation({
53
+ locale,
54
+ namespace,
55
+ key,
56
+ options: existsOptions,
57
+ dictionaryContent
58
+ }) !== void 0,
59
+ t: translate,
60
+ getFixedT: (language, fixedNamespace) => (key, fixedOptions) => {
61
+ const resolved = resolveTranslation({
62
+ locale: language ?? locale,
63
+ namespace: fixedNamespace ?? namespace,
64
+ key,
65
+ options: fixedOptions,
66
+ dictionaryContent: (language ?? locale) === locale && (fixedNamespace ?? namespace) === namespace ? dictionaryContent : void 0
67
+ });
68
+ return resolved !== void 0 ? resolved : key;
69
+ }
70
+ }
71
+ };
72
+ };
73
+
74
+ //#endregion
75
+ export { createTranslationApi };
76
+ //# sourceMappingURL=createTranslationApi.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTranslationApi.mjs","names":[],"sources":["../../src/createTranslationApi.ts"],"sourcesContent":["import { getHTMLTextDir } from '@intlayer/core/localization';\nimport { resolveMessage } from '@intlayer/core/messageFormat';\nimport {\n getInterpolationValues,\n resolveTranslation,\n type TypedTFunction,\n} from '@intlayer/i18next';\nimport type {\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type { TOptions } from 'i18next';\n\n/** Untyped runtime `t()` produced by {@link createTranslationApi}. */\nexport type TranslateFunction = (\n key: string | string[],\n optionsOrDefaultValue?: TOptions | string,\n extraOptions?: TOptions\n) => string;\n\n/**\n * The `i18n` facade with `t` and `getFixedT` typed against the dictionary the\n * hook is bound to: `t` is the hook's translate function type `TFn`, and\n * `getFixedT` returns a fully-typed `t()` for the requested namespace (or the\n * hook's own `t()` when no namespace is given).\n */\nexport type TypedI18nFacade<TFn = TranslateFunction> = Omit<\n I18nFacade,\n 't' | 'getFixedT'\n> & {\n t: TFn;\n getFixedT: {\n <N extends DictionaryKeys>(\n language: string | null | undefined,\n fixedNamespace: N\n ): TypedTFunction<N>;\n (language?: string | null, fixedNamespace?: null): TFn;\n };\n};\n\n/** Result shape of the typed translation hooks: `{ t, i18n, ready }`. */\nexport type TypedTranslationResult<TFn = TranslateFunction> = {\n t: TFn;\n i18n: TypedI18nFacade<TFn>;\n ready: true;\n};\n\n/** `i18n` facade shape produced by {@link createTranslationApi}. */\nexport type I18nFacade = {\n language: string;\n languages: string[];\n resolvedLanguage: string;\n isInitialized: boolean;\n changeLanguage: (newLanguage: string) => Promise<void>;\n dir: (language?: string) => 'ltr' | 'rtl';\n exists: (key: string, existsOptions?: TOptions) => boolean;\n t: TranslateFunction;\n getFixedT: (\n language?: string | null,\n fixedNamespace?: string | null\n ) => (key: string, fixedOptions?: TOptions) => string;\n};\n\nexport type CreateTranslationApiParams = {\n /** Active locale. */\n locale: LocalesValues;\n /** Locale setter backing `i18n.changeLanguage`. */\n setLocale: (newLocale: LocalesValues) => void;\n /** Configured locales backing `i18n.languages`. */\n availableLocales: LocalesValues[];\n /** Default namespace (dictionary key) for `t()` lookups. */\n namespace: string;\n /** Optional prefix prepended to every `t()` key. */\n keyPrefix?: string;\n /**\n * Pre-resolved dictionary content for `namespace` — supplied by the\n * build-optimized `useDictionary` / `useDictionaryDynamic` variants so\n * lookups skip the runtime registry.\n */\n dictionaryContent?: unknown;\n};\n\n/**\n * Builds the `{ t, i18n }` pair shared by `useTranslation` (runtime\n * registry lookup) and `useDictionary` / `useDictionaryDynamic`\n * (build-time-imported dictionary content).\n *\n * Translation lookup goes through the shared i18next-dialect resolver:\n * namespace prefixes (`ns:key`), the `ns` option, plural suffixes\n * (`key_one`/`key_other` via `Intl.PluralRules`), context suffixes\n * (`key_male`), `$t()` nesting, `defaultValue` and `{{var}}` interpolation\n * are all supported.\n */\nexport const createTranslationApi = ({\n locale,\n setLocale,\n availableLocales,\n namespace,\n keyPrefix,\n dictionaryContent,\n}: CreateTranslationApiParams): {\n translate: TranslateFunction;\n i18n: I18nFacade;\n} => {\n const resolveSingleKey = (\n key: string,\n translateOptions?: TOptions\n ): unknown =>\n resolveTranslation({\n locale,\n namespace,\n key: keyPrefix ? `${keyPrefix}.${key}` : key,\n options: translateOptions,\n dictionaryContent,\n });\n\n const translate: TranslateFunction = (\n key,\n optionsOrDefaultValue,\n extraOptions\n ) => {\n const translateOptions: TOptions =\n typeof optionsOrDefaultValue === 'string'\n ? { defaultValue: optionsOrDefaultValue, ...extraOptions }\n : (optionsOrDefaultValue ?? {});\n\n const keys = Array.isArray(key) ? key : [key];\n\n for (const candidateKey of keys) {\n const resolved = resolveSingleKey(candidateKey, translateOptions);\n if (resolved !== undefined) return resolved as string;\n }\n\n const defaultValue = translateOptions.defaultValue;\n if (typeof defaultValue === 'string') {\n return resolveMessage(\n defaultValue,\n getInterpolationValues(translateOptions),\n locale,\n 'i18next'\n );\n }\n\n return keys[keys.length - 1] as string;\n };\n\n const i18n: I18nFacade = {\n language: locale as string,\n languages: (availableLocales ?? []) as string[],\n resolvedLanguage: locale as string,\n isInitialized: true,\n changeLanguage: async (newLanguage: string) => {\n setLocale(newLanguage as LocalesValues);\n },\n dir: (language?: string): 'ltr' | 'rtl' => {\n const direction = getHTMLTextDir((language ?? locale) as LocalesValues);\n return direction === 'rtl' ? 'rtl' : 'ltr';\n },\n exists: (key: string, existsOptions?: TOptions): boolean =>\n resolveTranslation({\n locale,\n namespace,\n key,\n options: existsOptions,\n dictionaryContent,\n }) !== undefined,\n t: translate,\n getFixedT:\n (language?: string | null, fixedNamespace?: string | null) =>\n (key: string, fixedOptions?: TOptions): string => {\n const resolved = resolveTranslation({\n locale: (language ?? locale) as LocalesValues,\n namespace: fixedNamespace ?? namespace,\n key,\n options: fixedOptions,\n dictionaryContent:\n (language ?? locale) === locale &&\n (fixedNamespace ?? namespace) === namespace\n ? dictionaryContent\n : undefined,\n });\n return resolved !== undefined ? (resolved as string) : key;\n },\n };\n\n return { translate, i18n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;AA6FA,MAAa,wBAAwB,EACnC,QACA,WACA,kBACA,WACA,WACA,wBAIG;CACH,MAAM,oBACJ,KACA,qBAEA,mBAAmB;EACjB;EACA;EACA,KAAK,YAAY,GAAG,UAAU,GAAG,QAAQ;EACzC,SAAS;EACT;EACD,CAAC;CAEJ,MAAM,aACJ,KACA,uBACA,iBACG;EACH,MAAM,mBACJ,OAAO,0BAA0B,WAC7B;GAAE,cAAc;GAAuB,GAAG;GAAc,GACvD,yBAAyB,EAAE;EAElC,MAAM,OAAO,MAAM,QAAQ,IAAI,GAAG,MAAM,CAAC,IAAI;AAE7C,OAAK,MAAM,gBAAgB,MAAM;GAC/B,MAAM,WAAW,iBAAiB,cAAc,iBAAiB;AACjE,OAAI,aAAa,OAAW,QAAO;;EAGrC,MAAM,eAAe,iBAAiB;AACtC,MAAI,OAAO,iBAAiB,SAC1B,QAAO,eACL,cACA,uBAAuB,iBAAiB,EACxC,QACA,UACD;AAGH,SAAO,KAAK,KAAK,SAAS;;AA0C5B,QAAO;EAAE;EAAW;GAtClB,UAAU;GACV,WAAY,oBAAoB,EAAE;GAClC,kBAAkB;GAClB,eAAe;GACf,gBAAgB,OAAO,gBAAwB;AAC7C,cAAU,YAA6B;;GAEzC,MAAM,aAAqC;AAEzC,WADkB,eAAgB,YAAY,OAC9B,KAAK,QAAQ,QAAQ;;GAEvC,SAAS,KAAa,kBACpB,mBAAmB;IACjB;IACA;IACA;IACA,SAAS;IACT;IACD,CAAC,KAAK;GACT,GAAG;GACH,YACG,UAA0B,oBAC1B,KAAa,iBAAoC;IAChD,MAAM,WAAW,mBAAmB;KAClC,QAAS,YAAY;KACrB,WAAW,kBAAkB;KAC7B;KACA,SAAS;KACT,oBACG,YAAY,YAAY,WACxB,kBAAkB,eAAe,YAC9B,oBACA;KACP,CAAC;AACF,WAAO,aAAa,SAAa,WAAsB;;GAIrC;EAAE"}
@@ -1,11 +1,11 @@
1
- import { useTranslation } from "./useTranslation.mjs";
1
+ import { useTranslationLoose } from "./useTranslation.mjs";
2
2
  import * as React from "react";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
 
5
5
  //#region src/helpers.tsx
6
6
  const I18nContext = React.createContext({ i18n: null });
7
7
  const Translation = ({ children, ns, keyPrefix }) => {
8
- const { t, i18n } = useTranslation(ns, { keyPrefix });
8
+ const { t, i18n } = useTranslationLoose(ns, { keyPrefix });
9
9
  return children(t, {
10
10
  i18n,
11
11
  lng: i18n.language
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.mjs","names":[],"sources":["../../src/helpers.tsx"],"sourcesContent":["import type { i18n, TFunction } from 'i18next';\nimport * as React from 'react';\nimport { useTranslation } from './useTranslation';\n\n// Context\nexport const I18nContext = React.createContext<{ i18n: i18n }>({\n i18n: null as unknown as i18n,\n});\n\nexport interface TranslationProps {\n children: (\n t: TFunction,\n options: { i18n: i18n; lng: string },\n ready: boolean\n ) => React.ReactNode;\n ns?: string | string[];\n keyPrefix?: string;\n}\n\n// Translation Render-Prop Component\nexport const Translation = ({\n children,\n ns,\n keyPrefix,\n}: TranslationProps): React.ReactElement => {\n const { t, i18n } = useTranslation(ns, { keyPrefix });\n return children(\n t as unknown as TFunction,\n { i18n: i18n as unknown as i18n, lng: i18n.language },\n true\n ) as React.ReactElement;\n};\n\n// nodesToString implementation\nexport const nodesToString = (\n children: React.ReactNode,\n _i18nOptions?: unknown,\n _i18n?: unknown,\n _i18nKey?: string\n): string => {\n if (typeof children === 'string' || typeof children === 'number') {\n return String(children);\n }\n if (!children) return '';\n if (Array.isArray(children)) {\n return children.map((c) => nodesToString(c)).join('');\n }\n const childElement = children as React.ReactElement<{\n children?: React.ReactNode;\n }>;\n\n if (childElement.props?.children) {\n return nodesToString(childElement.props.children);\n }\n\n return '';\n};\n\n// Global Instance Getters/Setters\nlet globalI18n: i18n | null = null;\nlet globalDefaults: unknown = {};\n\nexport const setI18n = (instance: i18n): void => {\n globalI18n = instance;\n};\n\nexport const getI18n = (): i18n => {\n return globalI18n as i18n;\n};\n\nexport const setDefaults = (options: unknown): void => {\n globalDefaults = options;\n};\n\nexport const getDefaults = (): unknown => {\n return globalDefaults;\n};\n\n// SSR Stubs\nexport const useSSR = (\n _initialI18nStore: unknown,\n _initialLanguage: string\n): void => {};\n\nexport const withSSR = () => {\n const Hoc = <P extends object>(\n WrappedComponent: React.ComponentType<P>\n ): React.ComponentType<P> => {\n const Component = (props: P) => <WrappedComponent {...props} />;\n const ExtendedComponent = Component as typeof Component & {\n getInitialProps: (ctx: unknown) => Promise<Record<string, unknown>>;\n };\n ExtendedComponent.getInitialProps = async (_ctx: unknown) => ({});\n return ExtendedComponent;\n };\n return Hoc;\n};\n\nexport const composeInitialProps =\n (_ForComponent: unknown) => async (_ctx: unknown) => ({});\n\nexport const getInitialProps = () => ({\n initialI18nStore: {},\n initialLanguage: 'en',\n});\n"],"mappings":";;;;;AAKA,MAAa,cAAc,MAAM,cAA8B,EAC7D,MAAM,MACP,CAAC;AAaF,MAAa,eAAe,EAC1B,UACA,IACA,gBAC0C;CAC1C,MAAM,EAAE,GAAG,SAAS,eAAe,IAAI,EAAE,WAAW,CAAC;AACrD,QAAO,SACL,GACA;EAAQ;EAAyB,KAAK,KAAK;EAAU,EACrD,KACD;;AAIH,MAAa,iBACX,UACA,cACA,OACA,aACW;AACX,KAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SACtD,QAAO,OAAO,SAAS;AAEzB,KAAI,CAAC,SAAU,QAAO;AACtB,KAAI,MAAM,QAAQ,SAAS,CACzB,QAAO,SAAS,KAAK,MAAM,cAAc,EAAE,CAAC,CAAC,KAAK,GAAG;CAEvD,MAAM,eAAe;AAIrB,KAAI,aAAa,OAAO,SACtB,QAAO,cAAc,aAAa,MAAM,SAAS;AAGnD,QAAO;;AAIT,IAAI,aAA0B;AAC9B,IAAI,iBAA0B,EAAE;AAEhC,MAAa,WAAW,aAAyB;AAC/C,cAAa;;AAGf,MAAa,gBAAsB;AACjC,QAAO;;AAGT,MAAa,eAAe,YAA2B;AACrD,kBAAiB;;AAGnB,MAAa,oBAA6B;AACxC,QAAO;;AAIT,MAAa,UACX,mBACA,qBACS;AAEX,MAAa,gBAAgB;CAC3B,MAAM,OACJ,qBAC2B;EAC3B,MAAM,aAAa,UAAa,oBAAC,kBAAD,EAAkB,GAAI,OAAS;EAC/D,MAAM,oBAAoB;AAG1B,oBAAkB,kBAAkB,OAAO,UAAmB,EAAE;AAChE,SAAO;;AAET,QAAO;;AAGT,MAAa,uBACV,kBAA2B,OAAO,UAAmB,EAAE;AAE1D,MAAa,yBAAyB;CACpC,kBAAkB,EAAE;CACpB,iBAAiB;CAClB"}
1
+ {"version":3,"file":"helpers.mjs","names":[],"sources":["../../src/helpers.tsx"],"sourcesContent":["import type { i18n, TFunction } from 'i18next';\nimport * as React from 'react';\nimport { useTranslationLoose } from './useTranslation';\n\n// Context\nexport const I18nContext = React.createContext<{ i18n: i18n }>({\n i18n: null as unknown as i18n,\n});\n\nexport interface TranslationProps {\n children: (\n t: TFunction,\n options: { i18n: i18n; lng: string },\n ready: boolean\n ) => React.ReactNode;\n ns?: string | string[];\n keyPrefix?: string;\n}\n\n// Translation Render-Prop Component\nexport const Translation = ({\n children,\n ns,\n keyPrefix,\n}: TranslationProps): React.ReactElement => {\n const { t, i18n } = useTranslationLoose(ns, { keyPrefix });\n return children(\n t as unknown as TFunction,\n { i18n: i18n as unknown as i18n, lng: i18n.language },\n true\n ) as React.ReactElement;\n};\n\n// nodesToString implementation\nexport const nodesToString = (\n children: React.ReactNode,\n _i18nOptions?: unknown,\n _i18n?: unknown,\n _i18nKey?: string\n): string => {\n if (typeof children === 'string' || typeof children === 'number') {\n return String(children);\n }\n if (!children) return '';\n if (Array.isArray(children)) {\n return children.map((c) => nodesToString(c)).join('');\n }\n const childElement = children as React.ReactElement<{\n children?: React.ReactNode;\n }>;\n\n if (childElement.props?.children) {\n return nodesToString(childElement.props.children);\n }\n\n return '';\n};\n\n// Global Instance Getters/Setters\nlet globalI18n: i18n | null = null;\nlet globalDefaults: unknown = {};\n\nexport const setI18n = (instance: i18n): void => {\n globalI18n = instance;\n};\n\nexport const getI18n = (): i18n => {\n return globalI18n as i18n;\n};\n\nexport const setDefaults = (options: unknown): void => {\n globalDefaults = options;\n};\n\nexport const getDefaults = (): unknown => {\n return globalDefaults;\n};\n\n// SSR Stubs\nexport const useSSR = (\n _initialI18nStore: unknown,\n _initialLanguage: string\n): void => {};\n\nexport const withSSR = () => {\n const Hoc = <P extends object>(\n WrappedComponent: React.ComponentType<P>\n ): React.ComponentType<P> => {\n const Component = (props: P) => <WrappedComponent {...props} />;\n const ExtendedComponent = Component as typeof Component & {\n getInitialProps: (ctx: unknown) => Promise<Record<string, unknown>>;\n };\n ExtendedComponent.getInitialProps = async (_ctx: unknown) => ({});\n return ExtendedComponent;\n };\n return Hoc;\n};\n\nexport const composeInitialProps =\n (_ForComponent: unknown) => async (_ctx: unknown) => ({});\n\nexport const getInitialProps = () => ({\n initialI18nStore: {},\n initialLanguage: 'en',\n});\n"],"mappings":";;;;;AAKA,MAAa,cAAc,MAAM,cAA8B,EAC7D,MAAM,MACP,CAAC;AAaF,MAAa,eAAe,EAC1B,UACA,IACA,gBAC0C;CAC1C,MAAM,EAAE,GAAG,SAAS,oBAAoB,IAAI,EAAE,WAAW,CAAC;AAC1D,QAAO,SACL,GACA;EAAQ;EAAyB,KAAK,KAAK;EAAU,EACrD,KACD;;AAIH,MAAa,iBACX,UACA,cACA,OACA,aACW;AACX,KAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SACtD,QAAO,OAAO,SAAS;AAEzB,KAAI,CAAC,SAAU,QAAO;AACtB,KAAI,MAAM,QAAQ,SAAS,CACzB,QAAO,SAAS,KAAK,MAAM,cAAc,EAAE,CAAC,CAAC,KAAK,GAAG;CAEvD,MAAM,eAAe;AAIrB,KAAI,aAAa,OAAO,SACtB,QAAO,cAAc,aAAa,MAAM,SAAS;AAGnD,QAAO;;AAIT,IAAI,aAA0B;AAC9B,IAAI,iBAA0B,EAAE;AAEhC,MAAa,WAAW,aAAyB;AAC/C,cAAa;;AAGf,MAAa,gBAAsB;AACjC,QAAO;;AAGT,MAAa,eAAe,YAA2B;AACrD,kBAAiB;;AAGnB,MAAa,oBAA6B;AACxC,QAAO;;AAIT,MAAa,UACX,mBACA,qBACS;AAEX,MAAa,gBAAgB;CAC3B,MAAM,OACJ,qBAC2B;EAC3B,MAAM,aAAa,UAAa,oBAAC,kBAAD,EAAkB,GAAI,OAAS;EAC/D,MAAM,oBAAoB;AAG1B,oBAAkB,kBAAkB,OAAO,UAAmB,EAAE;AAChE,SAAO;;AAET,QAAO;;AAGT,MAAa,uBACV,kBAA2B,OAAO,UAAmB,EAAE;AAE1D,MAAa,yBAAyB;CACpC,kBAAkB,EAAE;CACpB,iBAAiB;CAClB"}
@@ -1,33 +1,13 @@
1
1
  import * as ANSIColors from "@intlayer/config/colors";
2
2
  import { colorize, getAppLogger } from "@intlayer/config/logger";
3
3
  import { join } from "node:path";
4
- import { runOnce } from "@intlayer/chokidar/utils";
4
+ import { REACT_I18NEXT_CALLERS } from "@intlayer/config/callers";
5
5
  import { getConfiguration } from "@intlayer/config/node";
6
+ import { runOnce } from "@intlayer/engine/utils";
6
7
  import { intlayer } from "vite-intlayer";
7
8
 
8
9
  //#region src/plugin/index.ts
9
10
  /**
10
- * Caller configurations for `react-i18next`'s `useTranslation` hook.
11
- *
12
- * Tells the intlayer field-usage analyser how to extract the dictionary key
13
- * (namespace) and consumed fields from `useTranslation` call sites, enabling
14
- * accurate dictionary pruning for projects using `@intlayer/react-i18next`.
15
- */
16
- const REACT_I18NEXT_COMPAT_CALLERS = [{
17
- callerName: "useTranslation",
18
- importSources: ["react-i18next", "@intlayer/react-i18next"],
19
- namespace: {
20
- from: "argument",
21
- index: 0
22
- },
23
- keyPrefix: {
24
- from: "option",
25
- argumentIndex: 1,
26
- property: "keyPrefix"
27
- },
28
- translationFunction: "destructured-t"
29
- }];
30
- /**
31
11
  * A Vite plugin for react-i18next compat that wraps vite-intlayer
32
12
  * and configures resolve aliases for react-i18next and i18next.
33
13
  */
@@ -39,7 +19,7 @@ const reactI18nextVitePlugin = (options) => {
39
19
  }, { cacheTimeoutMs: 1e3 * 60 * 60 });
40
20
  const basePlugins = intlayer({
41
21
  ...options,
42
- compatCallers: [...options?.compatCallers ?? [], ...REACT_I18NEXT_COMPAT_CALLERS]
22
+ compatCallers: [...options?.compatCallers ?? [], ...REACT_I18NEXT_CALLERS]
43
23
  });
44
24
  const compatPlugin = {
45
25
  name: "vite-react-i18next-compat-plugin",
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugin/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { runOnce } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { PluginOption } from 'vite';\nimport { type CompatCallerConfig, intlayer } from 'vite-intlayer';\n\n/**\n * Caller configurations for `react-i18next`'s `useTranslation` hook.\n *\n * Tells the intlayer field-usage analyser how to extract the dictionary key\n * (namespace) and consumed fields from `useTranslation` call sites, enabling\n * accurate dictionary pruning for projects using `@intlayer/react-i18next`.\n */\nconst REACT_I18NEXT_COMPAT_CALLERS: CompatCallerConfig[] = [\n {\n callerName: 'useTranslation',\n importSources: ['react-i18next', '@intlayer/react-i18next'],\n namespace: { from: 'argument', index: 0 },\n keyPrefix: { from: 'option', argumentIndex: 1, property: 'keyPrefix' },\n translationFunction: 'destructured-t',\n },\n];\n\n/**\n * A Vite plugin for react-i18next compat that wraps vite-intlayer\n * and configures resolve aliases for react-i18next and i18next.\n */\nexport const reactI18nextVitePlugin = (\n options?: Parameters<typeof intlayer>[0]\n): PluginOption[] => {\n const intlayerConfig = getConfiguration();\n const appLogger = getAppLogger(intlayerConfig);\n\n runOnce(\n join(\n intlayerConfig.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-issues-invitation.lock'\n ),\n () => {\n appLogger([\n colorize(\n 'Please report any issues you met on GitHub:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://github.com/aymericzip/intlayer/issues',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n },\n {\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n }\n );\n\n const basePlugins = intlayer({\n ...options,\n compatCallers: [\n ...(options?.compatCallers ?? []),\n ...REACT_I18NEXT_COMPAT_CALLERS,\n ],\n });\n\n const compatPlugin: PluginOption = {\n name: 'vite-react-i18next-compat-plugin',\n config: () => {\n return {\n resolve: {\n alias: {\n 'react-i18next': '@intlayer/react-i18next',\n i18next: '@intlayer/i18next',\n },\n },\n };\n },\n };\n\n return [\n ...(Array.isArray(basePlugins) ? basePlugins : [basePlugins]),\n compatPlugin,\n ];\n};\n\nexport default reactI18nextVitePlugin;\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAM,+BAAqD,CACzD;CACE,YAAY;CACZ,eAAe,CAAC,iBAAiB,0BAA0B;CAC3D,WAAW;EAAE,MAAM;EAAY,OAAO;EAAG;CACzC,WAAW;EAAE,MAAM;EAAU,eAAe;EAAG,UAAU;EAAa;CACtE,qBAAqB;CACtB,CACF;;;;;AAMD,MAAa,0BACX,YACmB;CACnB,MAAM,iBAAiB,kBAAkB;CACzC,MAAM,YAAY,aAAa,eAAe;AAE9C,SACE,KACE,eAAe,OAAO,SACtB,aACA,SACA,kCACD,QACK;AACJ,YAAU,CACR,SACE,+CACA,WAAW,KACZ,EACD,SACE,iDACA,WAAW,WACZ,CACF,CAAC;IAEJ,EACE,gBAAgB,MAAO,KAAK,IAC7B,CACF;CAED,MAAM,cAAc,SAAS;EAC3B,GAAG;EACH,eAAe,CACb,GAAI,SAAS,iBAAiB,EAAE,EAChC,GAAG,6BACJ;EACF,CAAC;CAEF,MAAM,eAA6B;EACjC,MAAM;EACN,cAAc;AACZ,UAAO,EACL,SAAS,EACP,OAAO;IACL,iBAAiB;IACjB,SAAS;IACV,EACF,EACF;;EAEJ;AAED,QAAO,CACL,GAAI,MAAM,QAAQ,YAAY,GAAG,cAAc,CAAC,YAAY,EAC5D,aACD"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugin/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { REACT_I18NEXT_CALLERS } from '@intlayer/config/callers';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport { runOnce } from '@intlayer/engine/utils';\nimport type { PluginOption } from 'vite';\nimport { intlayer } from 'vite-intlayer';\n\n/**\n * A Vite plugin for react-i18next compat that wraps vite-intlayer\n * and configures resolve aliases for react-i18next and i18next.\n */\nexport const reactI18nextVitePlugin = (\n options?: Parameters<typeof intlayer>[0]\n): PluginOption[] => {\n const intlayerConfig = getConfiguration();\n const appLogger = getAppLogger(intlayerConfig);\n\n runOnce(\n join(\n intlayerConfig.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-issues-invitation.lock'\n ),\n () => {\n appLogger([\n colorize(\n 'Please report any issues you met on GitHub:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://github.com/aymericzip/intlayer/issues',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n },\n {\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n }\n );\n\n const basePlugins = intlayer({\n ...options,\n compatCallers: [\n ...(options?.compatCallers ?? []),\n ...REACT_I18NEXT_CALLERS,\n ],\n });\n\n const compatPlugin: PluginOption = {\n name: 'vite-react-i18next-compat-plugin',\n config: () => {\n return {\n resolve: {\n alias: {\n 'react-i18next': '@intlayer/react-i18next',\n i18next: '@intlayer/i18next',\n },\n },\n };\n },\n };\n\n return [\n ...(Array.isArray(basePlugins) ? basePlugins : [basePlugins]),\n compatPlugin,\n ];\n};\n\nexport default reactI18nextVitePlugin;\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,0BACX,YACmB;CACnB,MAAM,iBAAiB,kBAAkB;CACzC,MAAM,YAAY,aAAa,eAAe;AAE9C,SACE,KACE,eAAe,OAAO,SACtB,aACA,SACA,kCACD,QACK;AACJ,YAAU,CACR,SACE,+CACA,WAAW,KACZ,EACD,SACE,iDACA,WAAW,WACZ,CACF,CAAC;IAEJ,EACE,gBAAgB,MAAO,KAAK,IAC7B,CACF;CAED,MAAM,cAAc,SAAS;EAC3B,GAAG;EACH,eAAe,CACb,GAAI,SAAS,iBAAiB,EAAE,EAChC,GAAG,sBACJ;EACF,CAAC;CAEF,MAAM,eAA6B;EACjC,MAAM;EACN,cAAc;AACZ,UAAO,EACL,SAAS,EACP,OAAO;IACL,iBAAiB;IACjB,SAAS;IACV,EACF,EACF;;EAEJ;AAED,QAAO,CACL,GAAI,MAAM,QAAQ,YAAY,GAAG,cAAc,CAAC,YAAY,EAC5D,aACD"}
@@ -1,61 +1,53 @@
1
1
  'use client';
2
2
 
3
+ import { createTranslationApi } from "./createTranslationApi.mjs";
3
4
  import { useMemo } from "react";
4
5
  import { useDictionary as useDictionary$1, useLocale } from "react-intlayer";
5
6
 
6
7
  //#region src/useDictionary.ts
7
- const navigatePath = (objectValue, path) => {
8
- if (!path) return objectValue;
9
- let current = objectValue;
10
- for (const part of path.split(".")) {
11
- if (current === null || current === void 0 || typeof current !== "object") return;
12
- current = current[part];
13
- }
14
- return current;
15
- };
16
8
  /**
17
9
  * Dictionary-accepting variant of `useTranslation`.
18
10
  *
19
- * Used internally by the SWC optimization: instead of looking up the dictionary
20
- * at runtime by key, the SWC plugin pre-imports the dictionary JSON at build time
21
- * and passes it directly here. This enables tree-shaking of unused locale content.
11
+ * Used internally by the build-time optimization: instead of looking up the
12
+ * dictionary at runtime by key, the babel/swc plugin pre-imports the dictionary
13
+ * JSON at build time and passes it directly here. This enables tree-shaking of
14
+ * unused locale content.
22
15
  *
23
- * For a nested namespace (`useTranslation('about.counter')`), the SWC passes the
24
- * key prefix (`'counter'`) as the second argument.
16
+ * For a nested namespace (`useTranslation('about.counter')`), the plugin passes
17
+ * the key prefix (`'counter'`) as the second argument.
25
18
  *
26
- * @example (generated by SWC, not written manually)
19
+ * The returned `{ t, i18n }` matches `useTranslation` exactly (plural and
20
+ * context suffixes, `$t()` nesting, `defaultValue`, interpolation).
21
+ *
22
+ * @example (generated by the plugin, not written manually)
27
23
  * import _abc from '.intlayer/dictionaries/about.json' with { type: 'json' };
28
24
  * const { t } = useDictionary(_abc);
29
25
  */
30
- const useDictionary = (dictionary, keyPrefix, options) => {
26
+ const useDictionary = ((dictionary, keyPrefix, options) => {
31
27
  const content = useDictionary$1(dictionary);
32
28
  const { locale, setLocale, availableLocales } = useLocale();
33
- const i18n = useMemo(() => ({
34
- language: locale,
35
- languages: availableLocales ?? [],
36
- changeLanguage: async (lng) => {
37
- setLocale(lng);
38
- },
39
- isInitialized: true
29
+ const prefix = (typeof keyPrefix === "object" ? keyPrefix : options)?.keyPrefix ?? (typeof keyPrefix === "string" ? keyPrefix : void 0);
30
+ const { translate, i18n } = useMemo(() => createTranslationApi({
31
+ locale,
32
+ setLocale,
33
+ availableLocales: availableLocales ?? [],
34
+ namespace: dictionary.key,
35
+ keyPrefix: prefix,
36
+ dictionaryContent: content
40
37
  }), [
41
38
  locale,
39
+ setLocale,
42
40
  availableLocales,
43
- setLocale
41
+ dictionary.key,
42
+ prefix,
43
+ content
44
44
  ]);
45
- const prefix = (typeof keyPrefix === "object" ? keyPrefix : options)?.keyPrefix ?? (typeof keyPrefix === "string" ? keyPrefix : void 0);
46
- const t = (key, params) => {
47
- const lookupKey = prefix ? `${prefix}.${String(key)}` : String(key);
48
- const rawValue = navigatePath(content, lookupKey);
49
- const str = String(rawValue ?? lookupKey);
50
- if (!params) return str;
51
- return str.replace(/\{\{(\w+)\}\}/g, (_, name) => params[name] != null ? String(params[name]) : `{{${name}}}`);
52
- };
53
45
  return {
54
- t,
46
+ t: translate,
55
47
  i18n,
56
48
  ready: true
57
49
  };
58
- };
50
+ });
59
51
 
60
52
  //#endregion
61
53
  export { useDictionary };
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionary.mjs","names":["useDictionaryBase"],"sources":["../../src/useDictionary.ts"],"sourcesContent":["'use client';\n\nimport type { ValidDotPathsFor } from '@intlayer/core/transpiler';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { useMemo } from 'react';\nimport type { UseTranslationOptions } from 'react-i18next';\nimport { useDictionary as useDictionaryBase, useLocale } from 'react-intlayer';\n\nconst navigatePath = (objectValue: unknown, path: string): unknown => {\n if (!path) return objectValue;\n let current: unknown = objectValue;\n for (const part of path.split('.')) {\n if (\n current === null ||\n current === undefined ||\n typeof current !== 'object'\n ) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n};\n\n/**\n * Dictionary-accepting variant of `useTranslation`.\n *\n * Used internally by the SWC optimization: instead of looking up the dictionary\n * at runtime by key, the SWC plugin pre-imports the dictionary JSON at build time\n * and passes it directly here. This enables tree-shaking of unused locale content.\n *\n * For a nested namespace (`useTranslation('about.counter')`), the SWC passes the\n * key prefix (`'counter'`) as the second argument.\n *\n * @example (generated by SWC, not written manually)\n * import _abc from '.intlayer/dictionaries/about.json' with { type: 'json' };\n * const { t } = useDictionary(_abc);\n */\nexport const useDictionary = <T extends Dictionary>(\n dictionary: T,\n keyPrefix?: string | UseTranslationOptions<string>,\n options?: UseTranslationOptions<string>\n) => {\n const content = useDictionaryBase(dictionary);\n const { locale, setLocale, availableLocales } = useLocale();\n\n const i18n = useMemo(\n () => ({\n language: locale as string,\n languages: (availableLocales ?? []) as string[],\n changeLanguage: async (lng: string) => {\n setLocale(lng as LocalesValues);\n },\n isInitialized: true,\n }),\n [locale, availableLocales, setLocale]\n );\n\n const resolvedOptions = typeof keyPrefix === 'object' ? keyPrefix : options;\n const prefix =\n resolvedOptions?.keyPrefix ??\n (typeof keyPrefix === 'string' ? keyPrefix : undefined);\n\n const t = <P extends ValidDotPathsFor<any>>(\n key: P,\n params?: Record<string, unknown>\n ): string => {\n const lookupKey = prefix ? `${prefix}.${String(key)}` : String(key);\n const rawValue = navigatePath(content, lookupKey);\n const str = String(rawValue ?? lookupKey);\n\n if (!params) return str;\n\n return str.replace(/\\{\\{(\\w+)\\}\\}/g, (_, name) =>\n params[name] != null ? String(params[name]) : `{{${name}}}`\n );\n };\n\n return { t, i18n, ready: true };\n};\n"],"mappings":";;;;;;AASA,MAAM,gBAAgB,aAAsB,SAA0B;AACpE,KAAI,CAAC,KAAM,QAAO;CAClB,IAAI,UAAmB;AACvB,MAAK,MAAM,QAAQ,KAAK,MAAM,IAAI,EAAE;AAClC,MACE,YAAY,QACZ,YAAY,UACZ,OAAO,YAAY,SAEnB;AAEF,YAAW,QAAoC;;AAEjD,QAAO;;;;;;;;;;;;;;;;AAiBT,MAAa,iBACX,YACA,WACA,YACG;CACH,MAAM,UAAUA,gBAAkB,WAAW;CAC7C,MAAM,EAAE,QAAQ,WAAW,qBAAqB,WAAW;CAE3D,MAAM,OAAO,eACJ;EACL,UAAU;EACV,WAAY,oBAAoB,EAAE;EAClC,gBAAgB,OAAO,QAAgB;AACrC,aAAU,IAAqB;;EAEjC,eAAe;EAChB,GACD;EAAC;EAAQ;EAAkB;EAAU,CACtC;CAGD,MAAM,UADkB,OAAO,cAAc,WAAW,YAAY,UAEjD,cAChB,OAAO,cAAc,WAAW,YAAY;CAE/C,MAAM,KACJ,KACA,WACW;EACX,MAAM,YAAY,SAAS,GAAG,OAAO,GAAG,OAAO,IAAI,KAAK,OAAO,IAAI;EACnE,MAAM,WAAW,aAAa,SAAS,UAAU;EACjD,MAAM,MAAM,OAAO,YAAY,UAAU;AAEzC,MAAI,CAAC,OAAQ,QAAO;AAEpB,SAAO,IAAI,QAAQ,mBAAmB,GAAG,SACvC,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,GAAG,KAAK,KAAK,IACzD;;AAGH,QAAO;EAAE;EAAG;EAAM,OAAO;EAAM"}
1
+ {"version":3,"file":"useDictionary.mjs","names":["useDictionaryBase"],"sources":["../../src/useDictionary.ts"],"sourcesContent":["'use client';\n\nimport type { ScopedTFunction, TypedTFunction } from '@intlayer/i18next';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { useMemo } from 'react';\nimport type { UseTranslationOptions } from 'react-i18next';\nimport { useDictionary as useDictionaryBase, useLocale } from 'react-intlayer';\nimport {\n createTranslationApi,\n type TypedTranslationResult,\n} from './createTranslationApi';\n\n/**\n * Overload set for {@link useDictionary}: without a key prefix the returned\n * `t()` is typed against the dictionary's dot-paths; with a prefix (string or\n * `keyPrefix` option) the keys are relative dot-paths under that scope.\n */\ntype UseDictionary = {\n <T extends Dictionary>(\n dictionary: T,\n options?: UseTranslationOptions<undefined>\n ): TypedTranslationResult<TypedTFunction<T['key'] & DictionaryKeys>>;\n <T extends Dictionary, Prefix extends string>(\n dictionary: T,\n keyPrefix: Prefix | (UseTranslationOptions<Prefix> & { keyPrefix: Prefix }),\n options?: UseTranslationOptions<Prefix>\n ): TypedTranslationResult<ScopedTFunction<T['key'] & DictionaryKeys, Prefix>>;\n};\n\n/**\n * Dictionary-accepting variant of `useTranslation`.\n *\n * Used internally by the build-time optimization: instead of looking up the\n * dictionary at runtime by key, the babel/swc plugin pre-imports the dictionary\n * JSON at build time and passes it directly here. This enables tree-shaking of\n * unused locale content.\n *\n * For a nested namespace (`useTranslation('about.counter')`), the plugin passes\n * the key prefix (`'counter'`) as the second argument.\n *\n * The returned `{ t, i18n }` matches `useTranslation` exactly (plural and\n * context suffixes, `$t()` nesting, `defaultValue`, interpolation).\n *\n * @example (generated by the plugin, not written manually)\n * import _abc from '.intlayer/dictionaries/about.json' with { type: 'json' };\n * const { t } = useDictionary(_abc);\n */\nexport const useDictionary = (<T extends Dictionary>(\n dictionary: T,\n keyPrefix?: string | UseTranslationOptions<string>,\n options?: UseTranslationOptions<string>\n) => {\n const content = useDictionaryBase(dictionary);\n const { locale, setLocale, availableLocales } = useLocale();\n\n const resolvedOptions = typeof keyPrefix === 'object' ? keyPrefix : options;\n const prefix =\n resolvedOptions?.keyPrefix ??\n (typeof keyPrefix === 'string' ? keyPrefix : undefined);\n\n const { translate, i18n } = useMemo(\n () =>\n createTranslationApi({\n locale: locale as LocalesValues,\n setLocale: setLocale as (newLocale: LocalesValues) => void,\n availableLocales: (availableLocales ?? []) as LocalesValues[],\n namespace: dictionary.key,\n keyPrefix: prefix,\n dictionaryContent: content,\n }),\n [locale, setLocale, availableLocales, dictionary.key, prefix, content]\n );\n\n return { t: translate, i18n, ready: true };\n}) as UseDictionary;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,MAAa,kBACX,YACA,WACA,YACG;CACH,MAAM,UAAUA,gBAAkB,WAAW;CAC7C,MAAM,EAAE,QAAQ,WAAW,qBAAqB,WAAW;CAG3D,MAAM,UADkB,OAAO,cAAc,WAAW,YAAY,UAEjD,cAChB,OAAO,cAAc,WAAW,YAAY;CAE/C,MAAM,EAAE,WAAW,SAAS,cAExB,qBAAqB;EACX;EACG;EACX,kBAAmB,oBAAoB,EAAE;EACzC,WAAW,WAAW;EACtB,WAAW;EACX,mBAAmB;EACpB,CAAC,EACJ;EAAC;EAAQ;EAAW;EAAkB,WAAW;EAAK;EAAQ;EAAQ,CACvE;AAED,QAAO;EAAE,GAAG;EAAW;EAAM,OAAO;EAAM"}
@@ -1,46 +1,42 @@
1
1
  'use client';
2
2
 
3
+ import { createTranslationApi } from "./createTranslationApi.mjs";
3
4
  import { useMemo } from "react";
4
5
  import { useDictionaryDynamic as useDictionaryDynamic$1, useLocale } from "react-intlayer";
5
6
 
6
7
  //#region src/useDictionaryDynamic.ts
7
8
  /**
8
9
  * Dynamic dictionary-accepting variant of `useTranslation`.
10
+ *
11
+ * Counterpart to {@link useDictionary} for dictionaries imported lazily per
12
+ * locale. For a nested namespace (`useTranslation('about.counter')`), the
13
+ * plugin passes the key prefix (`'counter'`) as the third argument.
9
14
  */
10
- const useDictionaryDynamic = (dictionaryPromise, key, options) => {
15
+ const useDictionaryDynamic = ((dictionaryPromise, key, keyPrefix, options) => {
11
16
  const content = useDictionaryDynamic$1(dictionaryPromise, key);
12
17
  const { locale, setLocale, availableLocales } = useLocale();
13
- const i18n = useMemo(() => ({
14
- language: locale,
15
- languages: availableLocales ?? [],
16
- changeLanguage: async (lng) => {
17
- setLocale(lng);
18
- },
19
- isInitialized: true
18
+ const prefix = (typeof keyPrefix === "object" ? keyPrefix : options)?.keyPrefix ?? (typeof keyPrefix === "string" ? keyPrefix : void 0);
19
+ const { translate, i18n } = useMemo(() => createTranslationApi({
20
+ locale,
21
+ setLocale,
22
+ availableLocales: availableLocales ?? [],
23
+ namespace: key,
24
+ keyPrefix: prefix,
25
+ dictionaryContent: content
20
26
  }), [
21
27
  locale,
28
+ setLocale,
22
29
  availableLocales,
23
- setLocale
30
+ key,
31
+ prefix,
32
+ content
24
33
  ]);
25
- const prefix = options?.keyPrefix;
26
- const t = (lookup, params) => {
27
- const lookupKey = prefix ? `${prefix}.${String(lookup)}` : String(lookup);
28
- const parts = lookupKey.split(".");
29
- let current = content;
30
- for (const part of parts) {
31
- if (current == null) break;
32
- current = current[part];
33
- }
34
- const str = String(current ?? lookupKey);
35
- if (!params) return str;
36
- return str.replace(/\{\{(\w+)\}\}/g, (_, k) => params[k] != null ? String(params[k]) : `{{${k}}}`);
37
- };
38
34
  return {
39
- t,
35
+ t: translate,
40
36
  i18n,
41
37
  ready: true
42
38
  };
43
- };
39
+ });
44
40
 
45
41
  //#endregion
46
42
  export { useDictionaryDynamic };
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionaryDynamic.mjs","names":["useDictionaryDynamicBase"],"sources":["../../src/useDictionaryDynamic.ts"],"sourcesContent":["'use client';\n\nimport type { ValidDotPathsFor } from '@intlayer/core/transpiler';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type { StrictModeLocaleMap } from '@intlayer/types/module_augmentation';\nimport { useMemo } from 'react';\nimport type { UseTranslationOptions } from 'react-i18next';\nimport {\n useDictionaryDynamic as useDictionaryDynamicBase,\n useLocale,\n} from 'react-intlayer';\n\n/**\n * Dynamic dictionary-accepting variant of `useTranslation`.\n */\nexport const useDictionaryDynamic = <\n const T extends Dictionary,\n const K extends string,\n>(\n dictionaryPromise: StrictModeLocaleMap<() => Promise<T>>,\n key: K,\n options?: UseTranslationOptions<string>\n) => {\n const content = useDictionaryDynamicBase<T, any>(\n dictionaryPromise,\n key as any\n );\n const { locale, setLocale, availableLocales } = useLocale();\n\n const i18n = useMemo(\n () => ({\n language: locale as string,\n languages: (availableLocales ?? []) as string[],\n changeLanguage: async (lng: string) => {\n setLocale(lng as any);\n },\n isInitialized: true,\n }),\n [locale, availableLocales, setLocale]\n );\n\n const prefix = options?.keyPrefix;\n\n const t = <P extends ValidDotPathsFor<any>>(\n lookup: P,\n params?: Record<string, unknown>\n ): string => {\n const lookupKey = prefix ? `${prefix}.${String(lookup)}` : String(lookup);\n const parts = lookupKey.split('.');\n let current: any = content;\n\n for (const part of parts) {\n if (current == null) break;\n current = current[part];\n }\n\n const str = String(current ?? lookupKey);\n\n if (!params) return str;\n\n return str.replace(/\\{\\{(\\w+)\\}\\}/g, (_, k) =>\n params[k] != null ? String(params[k]) : `{{${k}}}`\n );\n };\n\n return { t, i18n, ready: true };\n};\n"],"mappings":";;;;;;;;;AAeA,MAAa,wBAIX,mBACA,KACA,YACG;CACH,MAAM,UAAUA,uBACd,mBACA,IACD;CACD,MAAM,EAAE,QAAQ,WAAW,qBAAqB,WAAW;CAE3D,MAAM,OAAO,eACJ;EACL,UAAU;EACV,WAAY,oBAAoB,EAAE;EAClC,gBAAgB,OAAO,QAAgB;AACrC,aAAU,IAAW;;EAEvB,eAAe;EAChB,GACD;EAAC;EAAQ;EAAkB;EAAU,CACtC;CAED,MAAM,SAAS,SAAS;CAExB,MAAM,KACJ,QACA,WACW;EACX,MAAM,YAAY,SAAS,GAAG,OAAO,GAAG,OAAO,OAAO,KAAK,OAAO,OAAO;EACzE,MAAM,QAAQ,UAAU,MAAM,IAAI;EAClC,IAAI,UAAe;AAEnB,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,WAAW,KAAM;AACrB,aAAU,QAAQ;;EAGpB,MAAM,MAAM,OAAO,WAAW,UAAU;AAExC,MAAI,CAAC,OAAQ,QAAO;AAEpB,SAAO,IAAI,QAAQ,mBAAmB,GAAG,MACvC,OAAO,MAAM,OAAO,OAAO,OAAO,GAAG,GAAG,KAAK,EAAE,IAChD;;AAGH,QAAO;EAAE;EAAG;EAAM,OAAO;EAAM"}
1
+ {"version":3,"file":"useDictionaryDynamic.mjs","names":["useDictionaryDynamicBase"],"sources":["../../src/useDictionaryDynamic.ts"],"sourcesContent":["'use client';\n\nimport type { ScopedTFunction, TypedTFunction } from '@intlayer/i18next';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DictionaryKeys,\n LocalesValues,\n StrictModeLocaleMap,\n} from '@intlayer/types/module_augmentation';\nimport { useMemo } from 'react';\nimport type { UseTranslationOptions } from 'react-i18next';\nimport {\n useDictionaryDynamic as useDictionaryDynamicBase,\n useLocale,\n} from 'react-intlayer';\nimport {\n createTranslationApi,\n type TypedTranslationResult,\n} from './createTranslationApi';\n\n/**\n * Overload set for {@link useDictionaryDynamic}: without a key prefix the\n * returned `t()` is typed against the dictionary's dot-paths; with a prefix\n * (string or `keyPrefix` option) the keys are relative dot-paths under it.\n */\ntype UseDictionaryDynamic = {\n <T extends Dictionary, K extends DictionaryKeys>(\n dictionaryPromise: StrictModeLocaleMap<() => Promise<T>>,\n key: K,\n options?: UseTranslationOptions<undefined>\n ): TypedTranslationResult<TypedTFunction<K>>;\n <T extends Dictionary, K extends DictionaryKeys, Prefix extends string>(\n dictionaryPromise: StrictModeLocaleMap<() => Promise<T>>,\n key: K,\n keyPrefix: Prefix | (UseTranslationOptions<Prefix> & { keyPrefix: Prefix }),\n options?: UseTranslationOptions<Prefix>\n ): TypedTranslationResult<ScopedTFunction<K, Prefix>>;\n};\n\n/**\n * Dynamic dictionary-accepting variant of `useTranslation`.\n *\n * Counterpart to {@link useDictionary} for dictionaries imported lazily per\n * locale. For a nested namespace (`useTranslation('about.counter')`), the\n * plugin passes the key prefix (`'counter'`) as the third argument.\n */\nexport const useDictionaryDynamic = (<\n const T extends Dictionary,\n const K extends DictionaryKeys,\n>(\n dictionaryPromise: StrictModeLocaleMap<() => Promise<T>>,\n key: K,\n keyPrefix?: string | UseTranslationOptions<string>,\n options?: UseTranslationOptions<string>\n) => {\n const content = useDictionaryDynamicBase<T, K>(dictionaryPromise, key);\n const { locale, setLocale, availableLocales } = useLocale();\n\n const resolvedOptions = typeof keyPrefix === 'object' ? keyPrefix : options;\n const prefix =\n resolvedOptions?.keyPrefix ??\n (typeof keyPrefix === 'string' ? keyPrefix : undefined);\n\n const { translate, i18n } = useMemo(\n () =>\n createTranslationApi({\n locale: locale as LocalesValues,\n setLocale: setLocale as (newLocale: LocalesValues) => void,\n availableLocales: (availableLocales ?? []) as LocalesValues[],\n namespace: key,\n keyPrefix: prefix,\n dictionaryContent: content,\n }),\n [locale, setLocale, availableLocales, key, prefix, content]\n );\n\n return { t: translate, i18n, ready: true };\n}) as UseDictionaryDynamic;\n"],"mappings":";;;;;;;;;;;;;;AA8CA,MAAa,yBAIX,mBACA,KACA,WACA,YACG;CACH,MAAM,UAAUA,uBAA+B,mBAAmB,IAAI;CACtE,MAAM,EAAE,QAAQ,WAAW,qBAAqB,WAAW;CAG3D,MAAM,UADkB,OAAO,cAAc,WAAW,YAAY,UAEjD,cAChB,OAAO,cAAc,WAAW,YAAY;CAE/C,MAAM,EAAE,WAAW,SAAS,cAExB,qBAAqB;EACX;EACG;EACX,kBAAmB,oBAAoB,EAAE;EACzC,WAAW;EACX,WAAW;EACX,mBAAmB;EACpB,CAAC,EACJ;EAAC;EAAQ;EAAW;EAAkB;EAAK;EAAQ;EAAQ,CAC5D;AAED,QAAO;EAAE,GAAG;EAAW;EAAM,OAAO;EAAM"}
@@ -1,8 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { getHTMLTextDir } from "@intlayer/core/localization";
4
- import { resolveMessage } from "@intlayer/core/messageFormat";
5
- import { getInterpolationValues, resolveTranslation } from "@intlayer/i18next";
3
+ import { createTranslationApi } from "./createTranslationApi.mjs";
6
4
  import { useMemo } from "react";
7
5
  import { useLocale } from "react-intlayer";
8
6
 
@@ -17,91 +15,48 @@ import { useLocale } from "react-intlayer";
17
15
  * are all supported.
18
16
  *
19
17
  * The returned `t()` is typed against the intlayer dictionary for namespace N:
20
- * keys are autocompleted and dot-paths are validated at compile time.
18
+ * keys are autocompleted, dot-paths are validated at compile time, and the
19
+ * return type is resolved from the content at the key's path (with
20
+ * `returnObjects: true` the raw content subtree type is returned).
21
21
  *
22
22
  * @example
23
23
  * ```tsx
24
24
  * const { t } = useTranslation('about');
25
- * t('counter.label'); // ✓ typed; compile error if key doesn't exist
25
+ * t('counter.label'); // ✓ typed key and return value
26
26
  * t('items', { count: 3 }); // plural suffix resolution
27
27
  * ```
28
28
  */
29
- const useTranslation = (ns, options) => {
29
+ const useTranslationImplementation = (ns, options) => {
30
30
  const namespace = Array.isArray(ns) ? ns[0] ?? "translation" : ns ?? "translation";
31
31
  const { locale, setLocale, availableLocales } = useLocale();
32
32
  const keyPrefix = options?.keyPrefix;
33
- /**
34
- * @param key - A valid dot-notation path within the namespace N dictionary.
35
- * @param optionsOrDefaultValue - i18next `t()` options (interpolation
36
- * values, `count`, `context`, `ns`, `defaultValue`, …) or a default
37
- * value string.
38
- */
39
- const translate = useMemo(() => {
40
- const resolveSingleKey = (key, translateOptions) => resolveTranslation({
41
- locale,
42
- namespace,
43
- key: keyPrefix ? `${keyPrefix}.${key}` : key,
44
- options: translateOptions
45
- });
46
- return (key, optionsOrDefaultValue, extraOptions) => {
47
- const translateOptions = typeof optionsOrDefaultValue === "string" ? {
48
- defaultValue: optionsOrDefaultValue,
49
- ...extraOptions
50
- } : optionsOrDefaultValue ?? {};
51
- const keys = Array.isArray(key) ? key : [key];
52
- for (const candidateKey of keys) {
53
- const resolved = resolveSingleKey(candidateKey, translateOptions);
54
- if (resolved !== void 0) return resolved;
55
- }
56
- const defaultValue = translateOptions.defaultValue;
57
- if (typeof defaultValue === "string") return resolveMessage(defaultValue, getInterpolationValues(translateOptions), locale, "i18next");
58
- return keys[keys.length - 1];
59
- };
60
- }, [
33
+ const { translate, i18n } = useMemo(() => createTranslationApi({
61
34
  locale,
35
+ setLocale,
36
+ availableLocales: availableLocales ?? [],
37
+ namespace,
38
+ keyPrefix
39
+ }), [
40
+ locale,
41
+ setLocale,
42
+ availableLocales,
62
43
  namespace,
63
44
  keyPrefix
64
45
  ]);
65
46
  return {
66
47
  t: translate,
67
- i18n: useMemo(() => ({
68
- language: locale,
69
- languages: availableLocales ?? [],
70
- resolvedLanguage: locale,
71
- isInitialized: true,
72
- changeLanguage: async (newLanguage) => {
73
- setLocale(newLanguage);
74
- },
75
- dir: (language) => {
76
- return getHTMLTextDir(language ?? locale) === "rtl" ? "rtl" : "ltr";
77
- },
78
- exists: (key, existsOptions) => resolveTranslation({
79
- locale,
80
- namespace,
81
- key,
82
- options: existsOptions
83
- }) !== void 0,
84
- t: translate,
85
- getFixedT: (language, fixedNamespace) => (key, fixedOptions) => {
86
- const resolved = resolveTranslation({
87
- locale: language ?? locale,
88
- namespace: fixedNamespace ?? namespace,
89
- key,
90
- options: fixedOptions
91
- });
92
- return resolved !== void 0 ? resolved : key;
93
- }
94
- }), [
95
- locale,
96
- availableLocales,
97
- setLocale,
98
- namespace,
99
- translate
100
- ]),
48
+ i18n,
101
49
  ready: true
102
50
  };
103
51
  };
52
+ const useTranslation = useTranslationImplementation;
53
+ /**
54
+ * Loosely-typed view of {@link useTranslation} used by render-prop and HOC
55
+ * wrappers (`Translation`, `withTranslation`, `Trans`) that forward `t` to
56
+ * consumer-typed surfaces.
57
+ */
58
+ const useTranslationLoose = useTranslationImplementation;
104
59
 
105
60
  //#endregion
106
- export { useTranslation };
61
+ export { useTranslation, useTranslationLoose };
107
62
  //# sourceMappingURL=useTranslation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useTranslation.mjs","names":[],"sources":["../../src/useTranslation.ts"],"sourcesContent":["'use client';\n\nimport { getHTMLTextDir } from '@intlayer/core/localization';\nimport { resolveMessage } from '@intlayer/core/messageFormat';\nimport type { ValidDotPathsFor } from '@intlayer/core/transpiler';\nimport { getInterpolationValues, resolveTranslation } from '@intlayer/i18next';\nimport type {\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type { TOptions } from 'i18next';\nimport { useMemo } from 'react';\nimport type { UseTranslationOptions } from 'react-i18next';\nimport { useLocale } from 'react-intlayer';\n\n/**\n * Drop-in for react-i18next's `useTranslation`.\n *\n * Translation lookup goes through the shared i18next-dialect resolver:\n * namespace prefixes (`ns:key`), the `ns` option, plural suffixes\n * (`key_one`/`key_other` via `Intl.PluralRules`), context suffixes\n * (`key_male`), `$t()` nesting, `defaultValue` and `{{var}}` interpolation\n * are all supported.\n *\n * The returned `t()` is typed against the intlayer dictionary for namespace N:\n * keys are autocompleted and dot-paths are validated at compile time.\n *\n * @example\n * ```tsx\n * const { t } = useTranslation('about');\n * t('counter.label'); // ✓ typed; compile error if key doesn't exist\n * t('items', { count: 3 }); // plural suffix resolution\n * ```\n */\nexport const useTranslation = <N extends DictionaryKeys>(\n ns?: N | N[],\n options?: UseTranslationOptions<string>\n) => {\n const namespace = (\n Array.isArray(ns) ? (ns[0] ?? 'translation') : (ns ?? 'translation')\n ) as N;\n\n const { locale, setLocale, availableLocales } = useLocale();\n\n const keyPrefix = options?.keyPrefix;\n\n /**\n * @param key - A valid dot-notation path within the namespace N dictionary.\n * @param optionsOrDefaultValue - i18next `t()` options (interpolation\n * values, `count`, `context`, `ns`, `defaultValue`, …) or a default\n * value string.\n */\n const translate = useMemo(() => {\n const resolveSingleKey = (\n key: string,\n translateOptions?: TOptions\n ): unknown =>\n resolveTranslation({\n locale: locale as LocalesValues,\n namespace,\n key: keyPrefix ? `${keyPrefix}.${key}` : key,\n options: translateOptions,\n });\n\n return (\n key: string | string[],\n optionsOrDefaultValue?: TOptions | string,\n extraOptions?: TOptions\n ): string => {\n const translateOptions: TOptions =\n typeof optionsOrDefaultValue === 'string'\n ? { defaultValue: optionsOrDefaultValue, ...extraOptions }\n : (optionsOrDefaultValue ?? {});\n\n const keys = Array.isArray(key) ? key : [key];\n\n for (const candidateKey of keys) {\n const resolved = resolveSingleKey(candidateKey, translateOptions);\n if (resolved !== undefined) return resolved as string;\n }\n\n const defaultValue = translateOptions.defaultValue;\n if (typeof defaultValue === 'string') {\n return resolveMessage(\n defaultValue,\n getInterpolationValues(translateOptions),\n locale as LocalesValues,\n 'i18next'\n );\n }\n\n return keys[keys.length - 1];\n };\n }, [locale, namespace, keyPrefix]);\n\n const i18n = useMemo(\n () => ({\n language: locale as string,\n languages: (availableLocales ?? []) as string[],\n resolvedLanguage: locale as string,\n isInitialized: true,\n changeLanguage: async (newLanguage: string) => {\n setLocale(newLanguage as LocalesValues);\n },\n dir: (language?: string): 'ltr' | 'rtl' => {\n const direction = getHTMLTextDir((language ?? locale) as LocalesValues);\n return direction === 'rtl' ? 'rtl' : 'ltr';\n },\n exists: (key: string, existsOptions?: TOptions): boolean =>\n resolveTranslation({\n locale: locale as LocalesValues,\n namespace,\n key,\n options: existsOptions,\n }) !== undefined,\n t: translate,\n getFixedT:\n (language?: string | null, fixedNamespace?: string | null) =>\n (key: string, fixedOptions?: TOptions): string => {\n const resolved = resolveTranslation({\n locale: (language ?? locale) as LocalesValues,\n namespace: fixedNamespace ?? namespace,\n key,\n options: fixedOptions,\n });\n return resolved !== undefined ? (resolved as string) : key;\n },\n }),\n [locale, availableLocales, setLocale, namespace, translate]\n );\n\n /** Typed facade over the untyped runtime translate function. */\n const t = translate as <P extends ValidDotPathsFor<N>>(\n key: P | P[],\n optionsOrDefaultValue?: TOptions | string,\n extraOptions?: TOptions\n ) => string;\n\n return { t, i18n, ready: true };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,kBACX,IACA,YACG;CACH,MAAM,YACJ,MAAM,QAAQ,GAAG,GAAI,GAAG,MAAM,gBAAkB,MAAM;CAGxD,MAAM,EAAE,QAAQ,WAAW,qBAAqB,WAAW;CAE3D,MAAM,YAAY,SAAS;;;;;;;CAQ3B,MAAM,YAAY,cAAc;EAC9B,MAAM,oBACJ,KACA,qBAEA,mBAAmB;GACT;GACR;GACA,KAAK,YAAY,GAAG,UAAU,GAAG,QAAQ;GACzC,SAAS;GACV,CAAC;AAEJ,UACE,KACA,uBACA,iBACW;GACX,MAAM,mBACJ,OAAO,0BAA0B,WAC7B;IAAE,cAAc;IAAuB,GAAG;IAAc,GACvD,yBAAyB,EAAE;GAElC,MAAM,OAAO,MAAM,QAAQ,IAAI,GAAG,MAAM,CAAC,IAAI;AAE7C,QAAK,MAAM,gBAAgB,MAAM;IAC/B,MAAM,WAAW,iBAAiB,cAAc,iBAAiB;AACjE,QAAI,aAAa,OAAW,QAAO;;GAGrC,MAAM,eAAe,iBAAiB;AACtC,OAAI,OAAO,iBAAiB,SAC1B,QAAO,eACL,cACA,uBAAuB,iBAAiB,EACxC,QACA,UACD;AAGH,UAAO,KAAK,KAAK,SAAS;;IAE3B;EAAC;EAAQ;EAAW;EAAU,CAAC;AA6ClC,QAAO;EAAE;EAAG,MA3CC,eACJ;GACL,UAAU;GACV,WAAY,oBAAoB,EAAE;GAClC,kBAAkB;GAClB,eAAe;GACf,gBAAgB,OAAO,gBAAwB;AAC7C,cAAU,YAA6B;;GAEzC,MAAM,aAAqC;AAEzC,WADkB,eAAgB,YAAY,OAC9B,KAAK,QAAQ,QAAQ;;GAEvC,SAAS,KAAa,kBACpB,mBAAmB;IACT;IACR;IACA;IACA,SAAS;IACV,CAAC,KAAK;GACT,GAAG;GACH,YACG,UAA0B,oBAC1B,KAAa,iBAAoC;IAChD,MAAM,WAAW,mBAAmB;KAClC,QAAS,YAAY;KACrB,WAAW,kBAAkB;KAC7B;KACA,SAAS;KACV,CAAC;AACF,WAAO,aAAa,SAAa,WAAsB;;GAE5D,GACD;GAAC;GAAQ;GAAkB;GAAW;GAAW;GAAU,CAU7C;EAAE,OAAO;EAAM"}
1
+ {"version":3,"file":"useTranslation.mjs","names":[],"sources":["../../src/useTranslation.ts"],"sourcesContent":["'use client';\n\nimport type { ScopedTFunction, TypedTFunction } from '@intlayer/i18next';\nimport type {\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { useMemo } from 'react';\nimport type { UseTranslationOptions } from 'react-i18next';\nimport { useLocale } from 'react-intlayer';\nimport {\n createTranslationApi,\n type TypedTranslationResult,\n} from './createTranslationApi';\n\n/**\n * Overload set for {@link useTranslation}: with a `keyPrefix` option the\n * returned `t()` accepts relative dot-paths under the prefix; otherwise keys\n * are the dictionary's full dot-paths. Return types are resolved from the\n * content at the key's path.\n */\ntype UseTranslation = {\n <N extends DictionaryKeys, Prefix extends string>(\n ns: N | N[],\n options: UseTranslationOptions<Prefix> & { keyPrefix: Prefix }\n ): TypedTranslationResult<ScopedTFunction<N, Prefix>>;\n <N extends DictionaryKeys>(\n ns?: N | N[],\n options?: UseTranslationOptions<undefined>\n ): TypedTranslationResult<TypedTFunction<N>>;\n};\n\n/**\n * Drop-in for react-i18next's `useTranslation`.\n *\n * Translation lookup goes through the shared i18next-dialect resolver:\n * namespace prefixes (`ns:key`), the `ns` option, plural suffixes\n * (`key_one`/`key_other` via `Intl.PluralRules`), context suffixes\n * (`key_male`), `$t()` nesting, `defaultValue` and `{{var}}` interpolation\n * are all supported.\n *\n * The returned `t()` is typed against the intlayer dictionary for namespace N:\n * keys are autocompleted, dot-paths are validated at compile time, and the\n * return type is resolved from the content at the key's path (with\n * `returnObjects: true` the raw content subtree type is returned).\n *\n * @example\n * ```tsx\n * const { t } = useTranslation('about');\n * t('counter.label'); // ✓ typed key and return value\n * t('items', { count: 3 }); // plural suffix resolution\n * ```\n */\nconst useTranslationImplementation = (\n ns?: string | string[],\n options?: UseTranslationOptions<string>\n) => {\n const namespace = Array.isArray(ns)\n ? (ns[0] ?? 'translation')\n : (ns ?? 'translation');\n\n const { locale, setLocale, availableLocales } = useLocale();\n\n const keyPrefix = options?.keyPrefix;\n\n const { translate, i18n } = useMemo(\n () =>\n createTranslationApi({\n locale: locale as LocalesValues,\n setLocale: setLocale as (newLocale: LocalesValues) => void,\n availableLocales: (availableLocales ?? []) as LocalesValues[],\n namespace,\n keyPrefix,\n }),\n [locale, setLocale, availableLocales, namespace, keyPrefix]\n );\n\n return { t: translate, i18n, ready: true };\n};\n\nexport const useTranslation = useTranslationImplementation as UseTranslation;\n\n/**\n * Loosely-typed view of {@link useTranslation} used by render-prop and HOC\n * wrappers (`Translation`, `withTranslation`, `Trans`) that forward `t` to\n * consumer-typed surfaces.\n */\nexport const useTranslationLoose = useTranslationImplementation as (\n ns?: string | string[],\n options?: UseTranslationOptions<string>\n) => TypedTranslationResult;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,MAAM,gCACJ,IACA,YACG;CACH,MAAM,YAAY,MAAM,QAAQ,GAAG,GAC9B,GAAG,MAAM,gBACT,MAAM;CAEX,MAAM,EAAE,QAAQ,WAAW,qBAAqB,WAAW;CAE3D,MAAM,YAAY,SAAS;CAE3B,MAAM,EAAE,WAAW,SAAS,cAExB,qBAAqB;EACX;EACG;EACX,kBAAmB,oBAAoB,EAAE;EACzC;EACA;EACD,CAAC,EACJ;EAAC;EAAQ;EAAW;EAAkB;EAAW;EAAU,CAC5D;AAED,QAAO;EAAE,GAAG;EAAW;EAAM,OAAO;EAAM;;AAG5C,MAAa,iBAAiB;;;;;;AAO9B,MAAa,sBAAsB"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useTranslation } from "./useTranslation.mjs";
3
+ import { useTranslationLoose } from "./useTranslation.mjs";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
 
6
6
  //#region src/withTranslation.tsx
@@ -9,7 +9,7 @@ import { jsx } from "react/jsx-runtime";
9
9
  */
10
10
  const withTranslation = ((ns, options) => (WrappedComponent) => {
11
11
  const WithTranslation = (props) => {
12
- const { t, i18n } = useTranslation(ns, options);
12
+ const { t, i18n } = useTranslationLoose(ns, options);
13
13
  return /* @__PURE__ */ jsx(WrappedComponent, {
14
14
  ...props,
15
15
  t,
@@ -1 +1 @@
1
- {"version":3,"file":"withTranslation.mjs","names":[],"sources":["../../src/withTranslation.tsx"],"sourcesContent":["'use client';\n\nimport type * as React from 'react';\nimport type {\n withTranslation as _withTranslation,\n UseTranslationOptions,\n} from 'react-i18next';\nimport { useTranslation } from './useTranslation';\n\n/**\n * Drop-in for react-i18next's `withTranslation` HOC.\n */\nexport const withTranslation: typeof _withTranslation = ((\n ns?: string | string[],\n options?: UseTranslationOptions<string>\n) =>\n <P extends { t: unknown; i18n: unknown; tReady: boolean }>(\n WrappedComponent: React.ComponentType<P>\n ) => {\n const WithTranslation = (props: Omit<P, 't' | 'i18n' | 'tReady'>) => {\n const { t, i18n } = useTranslation(ns, options);\n return (\n <WrappedComponent\n {...(props as unknown as P)}\n t={t}\n i18n={i18n}\n tReady\n />\n );\n };\n WithTranslation.displayName = `withTranslation(${\n WrappedComponent.displayName ?? WrappedComponent.name ?? 'Component'\n })`;\n return WithTranslation as unknown as React.ComponentType<\n Omit<P, 't' | 'i18n' | 'tReady'>\n >;\n }) as typeof _withTranslation;\n"],"mappings":";;;;;;;;;AAYA,MAAa,oBACX,IACA,aAGE,qBACG;CACH,MAAM,mBAAmB,UAA4C;EACnE,MAAM,EAAE,GAAG,SAAS,eAAe,IAAI,QAAQ;AAC/C,SACE,oBAAC,kBAAD;GACE,GAAK;GACF;GACG;GACN;GACA;;AAGN,iBAAgB,cAAc,mBAC5B,iBAAiB,eAAe,iBAAiB,QAAQ,YAC1D;AACD,QAAO"}
1
+ {"version":3,"file":"withTranslation.mjs","names":[],"sources":["../../src/withTranslation.tsx"],"sourcesContent":["'use client';\n\nimport type * as React from 'react';\nimport type {\n withTranslation as _withTranslation,\n UseTranslationOptions,\n} from 'react-i18next';\nimport { useTranslationLoose } from './useTranslation';\n\n/**\n * Drop-in for react-i18next's `withTranslation` HOC.\n */\nexport const withTranslation: typeof _withTranslation = ((\n ns?: string | string[],\n options?: UseTranslationOptions<string>\n) =>\n <P extends { t: unknown; i18n: unknown; tReady: boolean }>(\n WrappedComponent: React.ComponentType<P>\n ) => {\n const WithTranslation = (props: Omit<P, 't' | 'i18n' | 'tReady'>) => {\n const { t, i18n } = useTranslationLoose(ns, options);\n return (\n <WrappedComponent\n {...(props as unknown as P)}\n t={t}\n i18n={i18n}\n tReady\n />\n );\n };\n WithTranslation.displayName = `withTranslation(${\n WrappedComponent.displayName ?? WrappedComponent.name ?? 'Component'\n })`;\n return WithTranslation as unknown as React.ComponentType<\n Omit<P, 't' | 'i18n' | 'tReady'>\n >;\n }) as typeof _withTranslation;\n"],"mappings":";;;;;;;;;AAYA,MAAa,oBACX,IACA,aAGE,qBACG;CACH,MAAM,mBAAmB,UAA4C;EACnE,MAAM,EAAE,GAAG,SAAS,oBAAoB,IAAI,QAAQ;AACpD,SACE,oBAAC,kBAAD;GACE,GAAK;GACF;GACG;GACN;GACA;;AAGN,iBAAgB,cAAc,mBAC5B,iBAAiB,eAAe,iBAAiB,QAAQ,YAC1D;AACD,QAAO"}
@@ -1,6 +1,28 @@
1
- import { Trans as Trans$1 } from "react-i18next";
1
+ import { TransProps } from "react-i18next";
2
+ import { ValidDotPathsFor } from "@intlayer/core/transpiler";
3
+ import { DictionaryKeys } from "@intlayer/types/module_augmentation";
4
+ import { ReactElement } from "react";
2
5
 
3
6
  //#region src/Trans.d.ts
7
+ /**
8
+ * `<Trans>` props for a known dictionary namespace: `i18nKey` is validated
9
+ * against the dictionary's dot-paths, matching the strength of `useIntlayer`.
10
+ */
11
+ type TypedTransProps<N extends DictionaryKeys, P extends string> = Omit<TransProps<string>, 'i18nKey' | 'ns'> & {
12
+ /** Dictionary namespace the `i18nKey` is validated against. */ns: N | readonly N[]; /** Typed dot-path key into the `ns` dictionary. */
13
+ i18nKey: P;
14
+ };
15
+ /**
16
+ * Overload set for {@link Trans}: with a `ns` prop the `i18nKey` is a typed
17
+ * dot-path of that dictionary; without one the key falls back to the default
18
+ * namespace and stays loosely typed.
19
+ */
20
+ type TypedTrans = {
21
+ <N extends DictionaryKeys, P extends ValidDotPathsFor<N>>(props: TypedTransProps<N, P>): ReactElement;
22
+ (props: TransProps<string> & {
23
+ ns?: undefined;
24
+ }): ReactElement;
25
+ };
4
26
  /**
5
27
  * Drop-in for react-i18next's `<Trans>`.
6
28
  *
@@ -16,7 +38,7 @@ import { Trans as Trans$1 } from "react-i18next";
16
38
  * </Trans>
17
39
  * ```
18
40
  */
19
- declare const Trans: typeof Trans$1;
41
+ declare const Trans: TypedTrans;
20
42
  //#endregion
21
43
  export { Trans };
22
44
  //# sourceMappingURL=Trans.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Trans.d.ts","names":[],"sources":["../../src/Trans.tsx"],"mappings":";;;;;AAsFA;;;;;;;;;;;;;cAAa,KAAA,SAAc,OAAA"}
1
+ {"version":3,"file":"Trans.d.ts","names":[],"sources":["../../src/Trans.tsx"],"mappings":";;;;;;;;AAiBgD;;KAO3C,eAAA,WAA0B,cAAA,sBAAoC,IAAA,CACjE,UAAA;EAD6B,+DAK7B,EAAA,EAAI,CAAA,YAAa,CAAA,IALgD;EAOjE,OAAA,EAAS,CAAA;AAAA;;;;;;KAQN,UAAA;EAAA,WACQ,cAAA,YAA0B,gBAAA,CAAiB,CAAA,GACpD,KAAA,EAAO,eAAA,CAAgB,CAAA,EAAG,CAAA,IACzB,YAAA;EAAA,CACF,KAAA,EAAO,UAAA;IAAuB,EAAA;EAAA,IAAmB,YAAA;AAAA;;;;;AAZxC;;;;;;;;;;;cAkFC,KAAA,EAuDR,UAAA"}
@@ -0,0 +1,76 @@
1
+ import { DictionaryKeys, LocalesValues } from "@intlayer/types/module_augmentation";
2
+ import { TypedTFunction } from "@intlayer/i18next";
3
+ import { TOptions } from "i18next";
4
+
5
+ //#region src/createTranslationApi.d.ts
6
+ /** Untyped runtime `t()` produced by {@link createTranslationApi}. */
7
+ type TranslateFunction = (key: string | string[], optionsOrDefaultValue?: TOptions | string, extraOptions?: TOptions) => string;
8
+ /**
9
+ * The `i18n` facade with `t` and `getFixedT` typed against the dictionary the
10
+ * hook is bound to: `t` is the hook's translate function type `TFn`, and
11
+ * `getFixedT` returns a fully-typed `t()` for the requested namespace (or the
12
+ * hook's own `t()` when no namespace is given).
13
+ */
14
+ type TypedI18nFacade<TFn = TranslateFunction> = Omit<I18nFacade, 't' | 'getFixedT'> & {
15
+ t: TFn;
16
+ getFixedT: {
17
+ <N extends DictionaryKeys>(language: string | null | undefined, fixedNamespace: N): TypedTFunction<N>;
18
+ (language?: string | null, fixedNamespace?: null): TFn;
19
+ };
20
+ };
21
+ /** Result shape of the typed translation hooks: `{ t, i18n, ready }`. */
22
+ type TypedTranslationResult<TFn = TranslateFunction> = {
23
+ t: TFn;
24
+ i18n: TypedI18nFacade<TFn>;
25
+ ready: true;
26
+ };
27
+ /** `i18n` facade shape produced by {@link createTranslationApi}. */
28
+ type I18nFacade = {
29
+ language: string;
30
+ languages: string[];
31
+ resolvedLanguage: string;
32
+ isInitialized: boolean;
33
+ changeLanguage: (newLanguage: string) => Promise<void>;
34
+ dir: (language?: string) => 'ltr' | 'rtl';
35
+ exists: (key: string, existsOptions?: TOptions) => boolean;
36
+ t: TranslateFunction;
37
+ getFixedT: (language?: string | null, fixedNamespace?: string | null) => (key: string, fixedOptions?: TOptions) => string;
38
+ };
39
+ type CreateTranslationApiParams = {
40
+ /** Active locale. */locale: LocalesValues; /** Locale setter backing `i18n.changeLanguage`. */
41
+ setLocale: (newLocale: LocalesValues) => void; /** Configured locales backing `i18n.languages`. */
42
+ availableLocales: LocalesValues[]; /** Default namespace (dictionary key) for `t()` lookups. */
43
+ namespace: string; /** Optional prefix prepended to every `t()` key. */
44
+ keyPrefix?: string;
45
+ /**
46
+ * Pre-resolved dictionary content for `namespace` — supplied by the
47
+ * build-optimized `useDictionary` / `useDictionaryDynamic` variants so
48
+ * lookups skip the runtime registry.
49
+ */
50
+ dictionaryContent?: unknown;
51
+ };
52
+ /**
53
+ * Builds the `{ t, i18n }` pair shared by `useTranslation` (runtime
54
+ * registry lookup) and `useDictionary` / `useDictionaryDynamic`
55
+ * (build-time-imported dictionary content).
56
+ *
57
+ * Translation lookup goes through the shared i18next-dialect resolver:
58
+ * namespace prefixes (`ns:key`), the `ns` option, plural suffixes
59
+ * (`key_one`/`key_other` via `Intl.PluralRules`), context suffixes
60
+ * (`key_male`), `$t()` nesting, `defaultValue` and `{{var}}` interpolation
61
+ * are all supported.
62
+ */
63
+ declare const createTranslationApi: ({
64
+ locale,
65
+ setLocale,
66
+ availableLocales,
67
+ namespace,
68
+ keyPrefix,
69
+ dictionaryContent
70
+ }: CreateTranslationApiParams) => {
71
+ translate: TranslateFunction;
72
+ i18n: I18nFacade;
73
+ };
74
+ //#endregion
75
+ export { CreateTranslationApiParams, I18nFacade, TranslateFunction, TypedI18nFacade, TypedTranslationResult, createTranslationApi };
76
+ //# sourceMappingURL=createTranslationApi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTranslationApi.d.ts","names":[],"sources":["../../src/createTranslationApi.ts"],"mappings":";;;;;;KAcY,iBAAA,IACV,GAAA,qBACA,qBAAA,GAAwB,QAAA,WACxB,YAAA,GAAe,QAAA;AAHjB;;;;;;AAAA,KAYY,eAAA,OAAsB,iBAAA,IAAqB,IAAA,CACrD,UAAA;EAGA,CAAA,EAAG,GAAA;EACH,SAAA;IAAA,WACa,cAAA,EACT,QAAA,6BACA,cAAA,EAAgB,CAAA,GACf,cAAA,CAAe,CAAA;IAAA,CACjB,QAAA,kBAA0B,cAAA,UAAwB,GAAA;EAAA;AAAA;;KAK3C,sBAAA,OAA6B,iBAAA;EACvC,CAAA,EAAG,GAAA;EACH,IAAA,EAAM,eAAA,CAAgB,GAAA;EACtB,KAAA;AAAA;;KAIU,UAAA;EACV,QAAA;EACA,SAAA;EACA,gBAAA;EACA,aAAA;EACA,cAAA,GAAiB,WAAA,aAAwB,OAAA;EACzC,GAAA,GAAM,QAAA;EACN,MAAA,GAAS,GAAA,UAAa,aAAA,GAAgB,QAAA;EACtC,CAAA,EAAG,iBAAA;EACH,SAAA,GACE,QAAA,kBACA,cAAA,sBACI,GAAA,UAAa,YAAA,GAAe,QAAA;AAAA;AAAA,KAGxB,0BAAA;EA/BP,qBAiCH,MAAA,EAAQ,aAAA,EAhCJ;EAkCJ,SAAA,GAAY,SAAA,EAAW,aAAA,WAjCnB;EAmCJ,gBAAA,EAAkB,aAAA,IAlCE;EAoCpB,SAAA,UAnC6B;EAqC7B,SAAA;EArCwD;;AAK1D;;;EAsCE,iBAAA;AAAA;;;;;;;;;;;;cAcW,oBAAA;EAAwB,MAAA;EAAA,SAAA;EAAA,gBAAA;EAAA,SAAA;EAAA,SAAA;EAAA;AAAA,GAOlC,0BAAA;EACD,SAAA,EAAW,iBAAA;EACX,IAAA,EAAM,UAAA;AAAA"}
@@ -1,5 +1,5 @@
1
- import { TFunction, i18n } from "i18next";
2
1
  import * as React from "react";
2
+ import { TFunction, i18n } from "i18next";
3
3
 
4
4
  //#region src/helpers.d.ts
5
5
  declare const I18nContext: React.Context<{
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/plugin/index.ts"],"mappings":";;;;;;AA6BA;;cAAa,sBAAA,GACX,OAAA,GAAU,UAAA,QAAkB,QAAA,SAC3B,YAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/plugin/index.ts"],"mappings":";;;;;;AAaA;;cAAa,sBAAA,GACX,OAAA,GAAU,UAAA,QAAkB,QAAA,SAC3B,YAAA"}
@@ -1,32 +1,40 @@
1
+ import { TypedTranslationResult } from "./createTranslationApi.js";
1
2
  import { UseTranslationOptions } from "react-i18next";
2
- import { ValidDotPathsFor } from "@intlayer/core/transpiler";
3
+ import { DictionaryKeys } from "@intlayer/types/module_augmentation";
4
+ import { ScopedTFunction, TypedTFunction } from "@intlayer/i18next";
3
5
  import { Dictionary } from "@intlayer/types/dictionary";
4
6
 
5
7
  //#region src/useDictionary.d.ts
8
+ /**
9
+ * Overload set for {@link useDictionary}: without a key prefix the returned
10
+ * `t()` is typed against the dictionary's dot-paths; with a prefix (string or
11
+ * `keyPrefix` option) the keys are relative dot-paths under that scope.
12
+ */
13
+ type UseDictionary = {
14
+ <T extends Dictionary>(dictionary: T, options?: UseTranslationOptions<undefined>): TypedTranslationResult<TypedTFunction<T['key'] & DictionaryKeys>>;
15
+ <T extends Dictionary, Prefix extends string>(dictionary: T, keyPrefix: Prefix | (UseTranslationOptions<Prefix> & {
16
+ keyPrefix: Prefix;
17
+ }), options?: UseTranslationOptions<Prefix>): TypedTranslationResult<ScopedTFunction<T['key'] & DictionaryKeys, Prefix>>;
18
+ };
6
19
  /**
7
20
  * Dictionary-accepting variant of `useTranslation`.
8
21
  *
9
- * Used internally by the SWC optimization: instead of looking up the dictionary
10
- * at runtime by key, the SWC plugin pre-imports the dictionary JSON at build time
11
- * and passes it directly here. This enables tree-shaking of unused locale content.
22
+ * Used internally by the build-time optimization: instead of looking up the
23
+ * dictionary at runtime by key, the babel/swc plugin pre-imports the dictionary
24
+ * JSON at build time and passes it directly here. This enables tree-shaking of
25
+ * unused locale content.
12
26
  *
13
- * For a nested namespace (`useTranslation('about.counter')`), the SWC passes the
14
- * key prefix (`'counter'`) as the second argument.
27
+ * For a nested namespace (`useTranslation('about.counter')`), the plugin passes
28
+ * the key prefix (`'counter'`) as the second argument.
15
29
  *
16
- * @example (generated by SWC, not written manually)
30
+ * The returned `{ t, i18n }` matches `useTranslation` exactly (plural and
31
+ * context suffixes, `$t()` nesting, `defaultValue`, interpolation).
32
+ *
33
+ * @example (generated by the plugin, not written manually)
17
34
  * import _abc from '.intlayer/dictionaries/about.json' with { type: 'json' };
18
35
  * const { t } = useDictionary(_abc);
19
36
  */
20
- declare const useDictionary: <T extends Dictionary>(dictionary: T, keyPrefix?: string | UseTranslationOptions<string>, options?: UseTranslationOptions<string>) => {
21
- t: <P extends ValidDotPathsFor<any>>(key: P, params?: Record<string, unknown>) => string;
22
- i18n: {
23
- language: string;
24
- languages: string[];
25
- changeLanguage: (lng: string) => Promise<void>;
26
- isInitialized: boolean;
27
- };
28
- ready: boolean;
29
- };
37
+ declare const useDictionary: UseDictionary;
30
38
  //#endregion
31
39
  export { useDictionary };
32
40
  //# sourceMappingURL=useDictionary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionary.d.ts","names":[],"sources":["../../src/useDictionary.ts"],"mappings":";;;;;;;AAuCA;;;;;;;;;;;;cAAa,aAAA,aAA2B,UAAA,EACtC,UAAA,EAAY,CAAA,EACZ,SAAA,YAAqB,qBAAA,UACrB,OAAA,GAAU,qBAAA;gBAsBW,gBAAA,OAAqB,GAAA,EACnC,CAAA,EAAC,MAAA,GACG,MAAA;;;;qCAf2B,OAAA"}
1
+ {"version":3,"file":"useDictionary.d.ts","names":[],"sources":["../../src/useDictionary.ts"],"mappings":";;;;;;;;;AAcgC;;;KAO3B,aAAA;EAAA,WACQ,UAAA,EACT,UAAA,EAAY,CAAA,EACZ,OAAA,GAAU,qBAAA,cACT,sBAAA,CAAuB,cAAA,CAAe,CAAA,UAAW,cAAA;EAAA,WACzC,UAAA,yBACT,UAAA,EAAY,CAAA,EACZ,SAAA,EAAW,MAAA,IAAU,qBAAA,CAAsB,MAAA;IAAY,SAAA,EAAW,MAAA;EAAA,IAClE,OAAA,GAAU,qBAAA,CAAsB,MAAA,IAC/B,sBAAA,CAAuB,eAAA,CAAgB,CAAA,UAAW,cAAA,EAAgB,MAAA;AAAA;;;;;;;;;;;;;;;;;;;cAqB1D,aAAA,EA2BP,aAAA"}
@@ -1,22 +1,29 @@
1
+ import { TypedTranslationResult } from "./createTranslationApi.js";
1
2
  import { UseTranslationOptions } from "react-i18next";
2
- import { ValidDotPathsFor } from "@intlayer/core/transpiler";
3
+ import { DictionaryKeys, StrictModeLocaleMap } from "@intlayer/types/module_augmentation";
4
+ import { ScopedTFunction, TypedTFunction } from "@intlayer/i18next";
3
5
  import { Dictionary } from "@intlayer/types/dictionary";
4
- import { StrictModeLocaleMap } from "@intlayer/types/module_augmentation";
5
6
 
6
7
  //#region src/useDictionaryDynamic.d.ts
7
8
  /**
8
- * Dynamic dictionary-accepting variant of `useTranslation`.
9
+ * Overload set for {@link useDictionaryDynamic}: without a key prefix the
10
+ * returned `t()` is typed against the dictionary's dot-paths; with a prefix
11
+ * (string or `keyPrefix` option) the keys are relative dot-paths under it.
9
12
  */
10
- declare const useDictionaryDynamic: <const T extends Dictionary, const K extends string>(dictionaryPromise: StrictModeLocaleMap<() => Promise<T>>, key: K, options?: UseTranslationOptions<string>) => {
11
- t: <P extends ValidDotPathsFor<any>>(lookup: P, params?: Record<string, unknown>) => string;
12
- i18n: {
13
- language: string;
14
- languages: string[];
15
- changeLanguage: (lng: string) => Promise<void>;
16
- isInitialized: boolean;
17
- };
18
- ready: boolean;
13
+ type UseDictionaryDynamic = {
14
+ <T extends Dictionary, K extends DictionaryKeys>(dictionaryPromise: StrictModeLocaleMap<() => Promise<T>>, key: K, options?: UseTranslationOptions<undefined>): TypedTranslationResult<TypedTFunction<K>>;
15
+ <T extends Dictionary, K extends DictionaryKeys, Prefix extends string>(dictionaryPromise: StrictModeLocaleMap<() => Promise<T>>, key: K, keyPrefix: Prefix | (UseTranslationOptions<Prefix> & {
16
+ keyPrefix: Prefix;
17
+ }), options?: UseTranslationOptions<Prefix>): TypedTranslationResult<ScopedTFunction<K, Prefix>>;
19
18
  };
19
+ /**
20
+ * Dynamic dictionary-accepting variant of `useTranslation`.
21
+ *
22
+ * Counterpart to {@link useDictionary} for dictionaries imported lazily per
23
+ * locale. For a nested namespace (`useTranslation('about.counter')`), the
24
+ * plugin passes the key prefix (`'counter'`) as the third argument.
25
+ */
26
+ declare const useDictionaryDynamic: UseDictionaryDynamic;
20
27
  //#endregion
21
28
  export { useDictionaryDynamic };
22
29
  //# sourceMappingURL=useDictionaryDynamic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionaryDynamic.d.ts","names":[],"sources":["../../src/useDictionaryDynamic.ts"],"mappings":";;;;;;;;AAeA;cAAa,oBAAA,mBACK,UAAA,0BAGhB,iBAAA,EAAmB,mBAAA,OAA0B,OAAA,CAAQ,CAAA,IACrD,GAAA,EAAK,CAAA,EACL,OAAA,GAAU,qBAAA;gBAsBW,gBAAA,OAAqB,MAAA,EAChC,CAAA,EAAC,MAAA,GACA,MAAA;;;;qCAZ2B,OAAA"}
1
+ {"version":3,"file":"useDictionaryDynamic.d.ts","names":[],"sources":["../../src/useDictionaryDynamic.ts"],"mappings":";;;;;;;;;AAkBgC;;;KAO3B,oBAAA;EAAA,WACQ,UAAA,YAAsB,cAAA,EAC/B,iBAAA,EAAmB,mBAAA,OAA0B,OAAA,CAAQ,CAAA,IACrD,GAAA,EAAK,CAAA,EACL,OAAA,GAAU,qBAAA,cACT,sBAAA,CAAuB,cAAA,CAAe,CAAA;EAAA,WAC9B,UAAA,YAAsB,cAAA,yBAC/B,iBAAA,EAAmB,mBAAA,OAA0B,OAAA,CAAQ,CAAA,IACrD,GAAA,EAAK,CAAA,EACL,SAAA,EAAW,MAAA,IAAU,qBAAA,CAAsB,MAAA;IAAY,SAAA,EAAW,MAAA;EAAA,IAClE,OAAA,GAAU,qBAAA,CAAsB,MAAA,IAC/B,sBAAA,CAAuB,eAAA,CAAgB,CAAA,EAAG,MAAA;AAAA;;;;;;;;cAUlC,oBAAA,EA+BP,oBAAA"}
@@ -1,43 +1,28 @@
1
+ import { TypedTranslationResult } from "./createTranslationApi.js";
1
2
  import { UseTranslationOptions } from "react-i18next";
2
- import { TOptions } from "i18next";
3
- import { ValidDotPathsFor } from "@intlayer/core/transpiler";
4
3
  import { DictionaryKeys } from "@intlayer/types/module_augmentation";
4
+ import { ScopedTFunction, TypedTFunction } from "@intlayer/i18next";
5
5
 
6
6
  //#region src/useTranslation.d.ts
7
7
  /**
8
- * Drop-in for react-i18next's `useTranslation`.
9
- *
10
- * Translation lookup goes through the shared i18next-dialect resolver:
11
- * namespace prefixes (`ns:key`), the `ns` option, plural suffixes
12
- * (`key_one`/`key_other` via `Intl.PluralRules`), context suffixes
13
- * (`key_male`), `$t()` nesting, `defaultValue` and `{{var}}` interpolation
14
- * are all supported.
15
- *
16
- * The returned `t()` is typed against the intlayer dictionary for namespace N:
17
- * keys are autocompleted and dot-paths are validated at compile time.
18
- *
19
- * @example
20
- * ```tsx
21
- * const { t } = useTranslation('about');
22
- * t('counter.label'); // ✓ typed; compile error if key doesn't exist
23
- * t('items', { count: 3 }); // plural suffix resolution
24
- * ```
8
+ * Overload set for {@link useTranslation}: with a `keyPrefix` option the
9
+ * returned `t()` accepts relative dot-paths under the prefix; otherwise keys
10
+ * are the dictionary's full dot-paths. Return types are resolved from the
11
+ * content at the key's path.
25
12
  */
26
- declare const useTranslation: <N extends DictionaryKeys>(ns?: N | N[], options?: UseTranslationOptions<string>) => {
27
- t: <P extends ValidDotPathsFor<N>>(key: P | P[], optionsOrDefaultValue?: TOptions | string, extraOptions?: TOptions) => string;
28
- i18n: {
29
- language: string;
30
- languages: string[];
31
- resolvedLanguage: string;
32
- isInitialized: boolean;
33
- changeLanguage: (newLanguage: string) => Promise<void>;
34
- dir: (language?: string) => "ltr" | "rtl";
35
- exists: (key: string, existsOptions?: TOptions) => boolean;
36
- t: (key: string | string[], optionsOrDefaultValue?: TOptions | string, extraOptions?: TOptions) => string;
37
- getFixedT: (language?: string | null, fixedNamespace?: string | null) => (key: string, fixedOptions?: TOptions) => string;
38
- };
39
- ready: boolean;
13
+ type UseTranslation = {
14
+ <N extends DictionaryKeys, Prefix extends string>(ns: N | N[], options: UseTranslationOptions<Prefix> & {
15
+ keyPrefix: Prefix;
16
+ }): TypedTranslationResult<ScopedTFunction<N, Prefix>>;
17
+ <N extends DictionaryKeys>(ns?: N | N[], options?: UseTranslationOptions<undefined>): TypedTranslationResult<TypedTFunction<N>>;
40
18
  };
19
+ declare const useTranslation: UseTranslation;
20
+ /**
21
+ * Loosely-typed view of {@link useTranslation} used by render-prop and HOC
22
+ * wrappers (`Translation`, `withTranslation`, `Trans`) that forward `t` to
23
+ * consumer-typed surfaces.
24
+ */
25
+ declare const useTranslationLoose: (ns?: string | string[], options?: UseTranslationOptions<string>) => TypedTranslationResult;
41
26
  //#endregion
42
- export { useTranslation };
27
+ export { useTranslation, useTranslationLoose };
43
28
  //# sourceMappingURL=useTranslation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useTranslation.d.ts","names":[],"sources":["../../src/useTranslation.ts"],"mappings":";;;;;;;;AAkCA;;;;;;;;;;;;;;;;;cAAa,cAAA,aAA4B,cAAA,EACvC,EAAA,GAAK,CAAA,GAAI,CAAA,IACT,OAAA,GAAU,qBAAA;gBAgGwB,gBAAA,CAAiB,CAAA,GAAE,GAAA,EAC9C,CAAA,GAAI,CAAA,IAAG,qBAAA,GACY,QAAA,WAAiB,YAAA,GAC1B,QAAA;;;;;;6CAlC6B,OAAA;;0BAOtB,aAAA,GAAkB,QAAA;gCA3ChB,qBAAA,GACE,QAAA,WAAiB,YAAA,GAC1B,QAAA;0CAkDY,cAAA,sBAAgC,GAAA,UAC7C,YAAA,GAAiB,QAAA;EAAA"}
1
+ {"version":3,"file":"useTranslation.d.ts","names":[],"sources":["../../src/useTranslation.ts"],"mappings":";;;;;;;;AAagC;;;;KAQ3B,cAAA;EAAA,WACQ,cAAA,yBACT,EAAA,EAAI,CAAA,GAAI,CAAA,IACR,OAAA,EAAS,qBAAA,CAAsB,MAAA;IAAY,SAAA,EAAW,MAAA;EAAA,IACrD,sBAAA,CAAuB,eAAA,CAAgB,CAAA,EAAG,MAAA;EAAA,WAClC,cAAA,EACT,EAAA,GAAK,CAAA,GAAI,CAAA,IACT,OAAA,GAAU,qBAAA,cACT,sBAAA,CAAuB,cAAA,CAAe,CAAA;AAAA;AAAA,cAmD9B,cAAA,EAAiD,cAAA;;;;;;cAOjD,mBAAA,GACX,EAAA,sBACA,OAAA,GAAU,qBAAA,aACP,sBAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/react-i18next",
3
- "version": "9.0.0-canary.11",
3
+ "version": "9.0.0-canary.13",
4
4
  "private": false,
5
5
  "description": "react-i18next API adapter for intlayer — useTranslation, Trans, withTranslation, I18nextProvider backed by react-intlayer",
6
6
  "keywords": [
@@ -76,13 +76,13 @@
76
76
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
77
77
  },
78
78
  "dependencies": {
79
- "@intlayer/chokidar": "9.0.0-canary.11",
80
- "@intlayer/config": "9.0.0-canary.11",
81
- "@intlayer/core": "9.0.0-canary.11",
82
- "@intlayer/i18next": "9.0.0-canary.11",
83
- "@intlayer/types": "9.0.0-canary.11",
84
- "react-intlayer": "9.0.0-canary.11",
85
- "vite-intlayer": "9.0.0-canary.11"
79
+ "@intlayer/config": "9.0.0-canary.13",
80
+ "@intlayer/core": "9.0.0-canary.13",
81
+ "@intlayer/engine": "9.0.0-canary.13",
82
+ "@intlayer/i18next": "9.0.0-canary.13",
83
+ "@intlayer/types": "9.0.0-canary.13",
84
+ "react-intlayer": "9.0.0-canary.13",
85
+ "vite-intlayer": "9.0.0-canary.13"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@types/node": "25.9.4",
@@ -95,7 +95,7 @@
95
95
  "rimraf": "6.1.3",
96
96
  "tsdown": "0.21.10",
97
97
  "typescript": "6.0.3",
98
- "vite": "8.1.0",
98
+ "vite": "8.1.3",
99
99
  "vitest": "4.1.9"
100
100
  },
101
101
  "peerDependencies": {