@intlayer/react-i18next 9.0.0-canary.10 → 9.0.0-canary.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/Trans.mjs +2 -2
- package/dist/esm/Trans.mjs.map +1 -1
- package/dist/esm/createTranslationApi.mjs +76 -0
- package/dist/esm/createTranslationApi.mjs.map +1 -0
- package/dist/esm/helpers.mjs +2 -2
- package/dist/esm/helpers.mjs.map +1 -1
- package/dist/esm/plugin/index.mjs +3 -23
- package/dist/esm/plugin/index.mjs.map +1 -1
- package/dist/esm/useDictionary.mjs +26 -34
- package/dist/esm/useDictionary.mjs.map +1 -1
- package/dist/esm/useDictionaryDynamic.mjs +20 -24
- package/dist/esm/useDictionaryDynamic.mjs.map +1 -1
- package/dist/esm/useTranslation.mjs +24 -69
- package/dist/esm/useTranslation.mjs.map +1 -1
- package/dist/esm/withTranslation.mjs +2 -2
- package/dist/esm/withTranslation.mjs.map +1 -1
- package/dist/types/Trans.d.ts +24 -2
- package/dist/types/Trans.d.ts.map +1 -1
- package/dist/types/createTranslationApi.d.ts +76 -0
- package/dist/types/createTranslationApi.d.ts.map +1 -0
- package/dist/types/helpers.d.ts +1 -1
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/useDictionary.d.ts +25 -17
- package/dist/types/useDictionary.d.ts.map +1 -1
- package/dist/types/useDictionaryDynamic.d.ts +19 -12
- package/dist/types/useDictionaryDynamic.d.ts.map +1 -1
- package/dist/types/useTranslation.d.ts +19 -34
- package/dist/types/useTranslation.d.ts.map +1 -1
- package/package.json +9 -9
package/dist/esm/Trans.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
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 } =
|
|
43
|
+
const { t } = useTranslationLoose(Array.isArray(ns) ? ns[0] : ns);
|
|
44
44
|
const translateOptions = {
|
|
45
45
|
...tOptions ?? {},
|
|
46
46
|
...values ?? {}
|
package/dist/esm/Trans.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Trans.mjs","names":[
|
|
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"}
|
package/dist/esm/helpers.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
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 } =
|
|
8
|
+
const { t, i18n } = useTranslationLoose(ns, { keyPrefix });
|
|
9
9
|
return children(t, {
|
|
10
10
|
i18n,
|
|
11
11
|
lng: i18n.language
|
package/dist/esm/helpers.mjs.map
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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 ?? [], ...
|
|
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 {
|
|
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
|
|
20
|
-
* at runtime by key, the
|
|
21
|
-
* and passes it directly here. This enables tree-shaking of
|
|
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
|
|
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
|
-
*
|
|
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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
|
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
|
|
25
|
+
* t('counter.label'); // ✓ typed key and return value
|
|
26
26
|
* t('items', { count: 3 }); // plural suffix resolution
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
|
-
const
|
|
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
|
|
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 {
|
|
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 {
|
|
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 } =
|
|
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 {
|
|
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"}
|
package/dist/types/Trans.d.ts
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
import {
|
|
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:
|
|
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":";;;;;
|
|
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"}
|
package/dist/types/helpers.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/plugin/index.ts"],"mappings":";;;;;;
|
|
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 {
|
|
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
|
|
10
|
-
* at runtime by key, the
|
|
11
|
-
* and passes it directly here. This enables tree-shaking of
|
|
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
|
|
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
|
-
*
|
|
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:
|
|
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":"
|
|
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 {
|
|
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
|
-
*
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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":"
|
|
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
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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":";;;;;;;;
|
|
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.
|
|
3
|
+
"version": "9.0.0-canary.12",
|
|
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/
|
|
80
|
-
"@intlayer/
|
|
81
|
-
"@intlayer/
|
|
82
|
-
"@intlayer/i18next": "9.0.0-canary.
|
|
83
|
-
"@intlayer/types": "9.0.0-canary.
|
|
84
|
-
"react-intlayer": "9.0.0-canary.
|
|
85
|
-
"vite-intlayer": "9.0.0-canary.
|
|
79
|
+
"@intlayer/config": "9.0.0-canary.12",
|
|
80
|
+
"@intlayer/core": "9.0.0-canary.12",
|
|
81
|
+
"@intlayer/engine": "9.0.0-canary.12",
|
|
82
|
+
"@intlayer/i18next": "9.0.0-canary.12",
|
|
83
|
+
"@intlayer/types": "9.0.0-canary.12",
|
|
84
|
+
"react-intlayer": "9.0.0-canary.12",
|
|
85
|
+
"vite-intlayer": "9.0.0-canary.12"
|
|
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.
|
|
98
|
+
"vite": "8.1.2",
|
|
99
99
|
"vitest": "4.1.9"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|