@vocab/react 1.1.13 → 1.1.14-fix-messageformat-import-20250923014407
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +66 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +14 -6
- package/README.md +0 -827
- package/dist/declarations/src/index.d.ts +0 -67
- package/dist/vocab-react.cjs.d.ts +0 -2
- package/dist/vocab-react.cjs.dev.js +0 -102
- package/dist/vocab-react.cjs.js +0 -7
- package/dist/vocab-react.cjs.prod.js +0 -102
- package/dist/vocab-react.esm.js +0 -92
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import { LanguageName, ParsedFormatFn, ParsedFormatFnByKey, TranslationFile } from "@vocab/core";
|
|
3
|
+
|
|
4
|
+
//#region src/components.d.ts
|
|
5
|
+
type Locale = string;
|
|
6
|
+
interface TranslationsContextValue {
|
|
7
|
+
/**
|
|
8
|
+
* The `language` passed in to your `VocabProvider`
|
|
9
|
+
*/
|
|
10
|
+
language: LanguageName;
|
|
11
|
+
/**
|
|
12
|
+
* The `locale` passed in to your `VocabProvider`
|
|
13
|
+
*
|
|
14
|
+
* Please note that this value will be `undefined` if you have not passed a `locale` to your `VocabProvider`.
|
|
15
|
+
* If your languages are named with IETF language tags, you should just use `language` instead of
|
|
16
|
+
* this value, unless you specifically need to access your `locale` override.
|
|
17
|
+
*/
|
|
18
|
+
locale?: Locale;
|
|
19
|
+
}
|
|
20
|
+
interface VocabProviderProps {
|
|
21
|
+
/**
|
|
22
|
+
* The language to load translations for. Must be one of the language names defined in your `vocab.config.js`.
|
|
23
|
+
*/
|
|
24
|
+
language: TranslationsContextValue['language'];
|
|
25
|
+
/**
|
|
26
|
+
* A locale override. By default, Vocab will use the `language` as the locale when formatting messages if
|
|
27
|
+
* `locale` is not set. If your languages are named with IETF language tags, you probably don't need to
|
|
28
|
+
* set this value.
|
|
29
|
+
*
|
|
30
|
+
* You may want to override the locale for a specific language if the default formatting for that locale
|
|
31
|
+
* is not desired.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Override the locale for th-TH to use the Gregorian calendar instead of the default Buddhist calendar
|
|
35
|
+
* <VocabProvider language="th-TH" locale="th-TH-u-ca-gregory">
|
|
36
|
+
* </App>
|
|
37
|
+
* <VocabProvider />
|
|
38
|
+
*/
|
|
39
|
+
locale?: TranslationsContextValue['locale'];
|
|
40
|
+
children: ReactNode;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Provides a translation context for your application
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* import { VocabProvider } from '@vocab/react';
|
|
47
|
+
*
|
|
48
|
+
* <VocabProvider language="en">
|
|
49
|
+
* <App />
|
|
50
|
+
* <VocabProvider />
|
|
51
|
+
*/
|
|
52
|
+
declare const VocabProvider: ({
|
|
53
|
+
children,
|
|
54
|
+
language,
|
|
55
|
+
locale
|
|
56
|
+
}: VocabProviderProps) => React.JSX.Element;
|
|
57
|
+
/**
|
|
58
|
+
* @returns The `language` and `locale` values passed in to your `VocabProvider`
|
|
59
|
+
*/
|
|
60
|
+
declare const useLanguage: () => TranslationsContextValue;
|
|
61
|
+
type FormatXMLElementReactNodeFn = (parts: ReactNode[]) => ReactNode;
|
|
62
|
+
type MapToReactNodeFunction<Params extends Record<string, any>> = { [key in keyof Params]: Params[key] extends ParsedFormatFn ? FormatXMLElementReactNodeFn : Params[key] };
|
|
63
|
+
type TranslateFn<FormatFnByKey extends ParsedFormatFnByKey> = {
|
|
64
|
+
<TranslationKey extends keyof FormatFnByKey>(key: TranslationKey, params: MapToReactNodeFunction<Parameters<FormatFnByKey[TranslationKey]>[0]>): ReturnType<FormatFnByKey[TranslationKey]> extends string ? string : ReactNode | string | Array<ReactNode | string>;
|
|
65
|
+
<TranslationKey extends keyof FormatFnByKey>(key: Parameters<FormatFnByKey[TranslationKey]>[0] extends Record<string, any> ? never : TranslationKey): string;
|
|
66
|
+
};
|
|
67
|
+
declare function useTranslations<Language extends string, FormatFnByKey extends ParsedFormatFnByKey>(translations: TranslationFile<Language, FormatFnByKey>): {
|
|
68
|
+
ready: boolean;
|
|
69
|
+
t: TranslateFn<FormatFnByKey>;
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { VocabProvider, useLanguage, useTranslations };
|
|
73
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { LanguageName, ParsedFormatFn, ParsedFormatFnByKey, TranslationFile } from "@vocab/core";
|
|
2
|
+
import React, { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/components.d.ts
|
|
5
|
+
type Locale = string;
|
|
6
|
+
interface TranslationsContextValue {
|
|
7
|
+
/**
|
|
8
|
+
* The `language` passed in to your `VocabProvider`
|
|
9
|
+
*/
|
|
10
|
+
language: LanguageName;
|
|
11
|
+
/**
|
|
12
|
+
* The `locale` passed in to your `VocabProvider`
|
|
13
|
+
*
|
|
14
|
+
* Please note that this value will be `undefined` if you have not passed a `locale` to your `VocabProvider`.
|
|
15
|
+
* If your languages are named with IETF language tags, you should just use `language` instead of
|
|
16
|
+
* this value, unless you specifically need to access your `locale` override.
|
|
17
|
+
*/
|
|
18
|
+
locale?: Locale;
|
|
19
|
+
}
|
|
20
|
+
interface VocabProviderProps {
|
|
21
|
+
/**
|
|
22
|
+
* The language to load translations for. Must be one of the language names defined in your `vocab.config.js`.
|
|
23
|
+
*/
|
|
24
|
+
language: TranslationsContextValue['language'];
|
|
25
|
+
/**
|
|
26
|
+
* A locale override. By default, Vocab will use the `language` as the locale when formatting messages if
|
|
27
|
+
* `locale` is not set. If your languages are named with IETF language tags, you probably don't need to
|
|
28
|
+
* set this value.
|
|
29
|
+
*
|
|
30
|
+
* You may want to override the locale for a specific language if the default formatting for that locale
|
|
31
|
+
* is not desired.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Override the locale for th-TH to use the Gregorian calendar instead of the default Buddhist calendar
|
|
35
|
+
* <VocabProvider language="th-TH" locale="th-TH-u-ca-gregory">
|
|
36
|
+
* </App>
|
|
37
|
+
* <VocabProvider />
|
|
38
|
+
*/
|
|
39
|
+
locale?: TranslationsContextValue['locale'];
|
|
40
|
+
children: ReactNode;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Provides a translation context for your application
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* import { VocabProvider } from '@vocab/react';
|
|
47
|
+
*
|
|
48
|
+
* <VocabProvider language="en">
|
|
49
|
+
* <App />
|
|
50
|
+
* <VocabProvider />
|
|
51
|
+
*/
|
|
52
|
+
declare const VocabProvider: ({
|
|
53
|
+
children,
|
|
54
|
+
language,
|
|
55
|
+
locale
|
|
56
|
+
}: VocabProviderProps) => React.JSX.Element;
|
|
57
|
+
/**
|
|
58
|
+
* @returns The `language` and `locale` values passed in to your `VocabProvider`
|
|
59
|
+
*/
|
|
60
|
+
declare const useLanguage: () => TranslationsContextValue;
|
|
61
|
+
type FormatXMLElementReactNodeFn = (parts: ReactNode[]) => ReactNode;
|
|
62
|
+
type MapToReactNodeFunction<Params extends Record<string, any>> = { [key in keyof Params]: Params[key] extends ParsedFormatFn ? FormatXMLElementReactNodeFn : Params[key] };
|
|
63
|
+
type TranslateFn<FormatFnByKey extends ParsedFormatFnByKey> = {
|
|
64
|
+
<TranslationKey extends keyof FormatFnByKey>(key: TranslationKey, params: MapToReactNodeFunction<Parameters<FormatFnByKey[TranslationKey]>[0]>): ReturnType<FormatFnByKey[TranslationKey]> extends string ? string : ReactNode | string | Array<ReactNode | string>;
|
|
65
|
+
<TranslationKey extends keyof FormatFnByKey>(key: Parameters<FormatFnByKey[TranslationKey]>[0] extends Record<string, any> ? never : TranslationKey): string;
|
|
66
|
+
};
|
|
67
|
+
declare function useTranslations<Language extends string, FormatFnByKey extends ParsedFormatFnByKey>(translations: TranslationFile<Language, FormatFnByKey>): {
|
|
68
|
+
ready: boolean;
|
|
69
|
+
t: TranslateFn<FormatFnByKey>;
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { VocabProvider, useLanguage, useTranslations };
|
|
73
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
let react = require("react");
|
|
25
|
+
react = __toESM(react);
|
|
26
|
+
|
|
27
|
+
//#region src/components.tsx
|
|
28
|
+
const TranslationsContext = react.default.createContext(void 0);
|
|
29
|
+
/**
|
|
30
|
+
* Provides a translation context for your application
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* import { VocabProvider } from '@vocab/react';
|
|
34
|
+
*
|
|
35
|
+
* <VocabProvider language="en">
|
|
36
|
+
* <App />
|
|
37
|
+
* <VocabProvider />
|
|
38
|
+
*/
|
|
39
|
+
const VocabProvider = ({ children, language, locale }) => {
|
|
40
|
+
const value = (0, react.useMemo)(() => ({
|
|
41
|
+
language,
|
|
42
|
+
locale
|
|
43
|
+
}), [language, locale]);
|
|
44
|
+
return /* @__PURE__ */ react.default.createElement(TranslationsContext.Provider, { value }, children);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* @returns The `language` and `locale` values passed in to your `VocabProvider`
|
|
48
|
+
*/
|
|
49
|
+
const useLanguage = () => {
|
|
50
|
+
const context = (0, react.useContext)(TranslationsContext);
|
|
51
|
+
if (!context) throw new Error("Attempted to access translation without Vocab context set. Did you forget to render VocabProvider?");
|
|
52
|
+
if (!context.language) throw new Error("Attempted to access translation without language set. Did you forget to pass language to VocabProvider?");
|
|
53
|
+
return context;
|
|
54
|
+
};
|
|
55
|
+
const SERVER_RENDERING = typeof window === "undefined";
|
|
56
|
+
function useTranslations(translations) {
|
|
57
|
+
const { language, locale } = useLanguage();
|
|
58
|
+
const [, forceRender] = (0, react.useReducer)((s) => s + 1, 0);
|
|
59
|
+
const translationsObject = translations.getLoadedMessages(language, locale || language);
|
|
60
|
+
let ready = true;
|
|
61
|
+
if (!translationsObject) {
|
|
62
|
+
if (SERVER_RENDERING) throw new Error(`Translations not synchronously available on server render. Applying translations dynamically server-side is not supported.`);
|
|
63
|
+
translations.load(language).then(() => {
|
|
64
|
+
forceRender();
|
|
65
|
+
});
|
|
66
|
+
ready = false;
|
|
67
|
+
}
|
|
68
|
+
const t = (0, react.useCallback)((key, params) => {
|
|
69
|
+
if (!translationsObject) return " ";
|
|
70
|
+
const message = translationsObject?.[key];
|
|
71
|
+
if (!message) {
|
|
72
|
+
console.error(`Unable to find translation for key "${key}". Possible keys are ${Object.keys(translationsObject).map((v) => `"${v}"`).join(", ")}`);
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
const result = message.format(params);
|
|
76
|
+
if (Array.isArray(result)) for (let i = 0; i < result.length; i++) {
|
|
77
|
+
const item = result[i];
|
|
78
|
+
if (typeof item === "object" && item && !item.key && (0, react.isValidElement)(item)) result[i] = (0, react.cloneElement)(item, { key: `_vocab-${i}` });
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}, [translationsObject]);
|
|
82
|
+
return {
|
|
83
|
+
ready,
|
|
84
|
+
t
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
exports.VocabProvider = VocabProvider;
|
|
90
|
+
exports.useLanguage = useLanguage;
|
|
91
|
+
exports.useTranslations = useTranslations;
|
|
92
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["React"],"sources":["../src/components.tsx"],"sourcesContent":["import type {\n TranslationFile,\n LanguageName,\n ParsedFormatFnByKey,\n ParsedFormatFn,\n} from '@vocab/core';\n\nimport React, {\n type ReactNode,\n useContext,\n useMemo,\n useReducer,\n isValidElement,\n cloneElement,\n useCallback,\n} from 'react';\n\ntype Locale = string;\n\ninterface TranslationsContextValue {\n /**\n * The `language` passed in to your `VocabProvider`\n */\n language: LanguageName;\n /**\n * The `locale` passed in to your `VocabProvider`\n *\n * Please note that this value will be `undefined` if you have not passed a `locale` to your `VocabProvider`.\n * If your languages are named with IETF language tags, you should just use `language` instead of\n * this value, unless you specifically need to access your `locale` override.\n */\n locale?: Locale;\n}\n\nconst TranslationsContext = React.createContext<\n TranslationsContextValue | undefined\n>(undefined);\n\n// Not extending TranslationsContextValue so we can tailor the docs for each prop to be better\n// suited to the provider, rather than for the useLanguage hook\ninterface VocabProviderProps {\n /**\n * The language to load translations for. Must be one of the language names defined in your `vocab.config.js`.\n */\n language: TranslationsContextValue['language'];\n /**\n * A locale override. By default, Vocab will use the `language` as the locale when formatting messages if\n * `locale` is not set. If your languages are named with IETF language tags, you probably don't need to\n * set this value.\n *\n * You may want to override the locale for a specific language if the default formatting for that locale\n * is not desired.\n *\n * @example\n * // Override the locale for th-TH to use the Gregorian calendar instead of the default Buddhist calendar\n * <VocabProvider language=\"th-TH\" locale=\"th-TH-u-ca-gregory\">\n * </App>\n * <VocabProvider />\n */\n locale?: TranslationsContextValue['locale'];\n children: ReactNode;\n}\n\n/**\n * Provides a translation context for your application\n *\n * @example\n * import { VocabProvider } from '@vocab/react';\n *\n * <VocabProvider language=\"en\">\n * <App />\n * <VocabProvider />\n */\nexport const VocabProvider = ({\n children,\n language,\n locale,\n}: VocabProviderProps) => {\n const value = useMemo(() => ({ language, locale }), [language, locale]);\n\n return (\n <TranslationsContext.Provider value={value}>\n {children}\n </TranslationsContext.Provider>\n );\n};\n\n/**\n * @returns The `language` and `locale` values passed in to your `VocabProvider`\n */\nexport const useLanguage = (): TranslationsContextValue => {\n const context = useContext(TranslationsContext);\n if (!context) {\n throw new Error(\n 'Attempted to access translation without Vocab context set. Did you forget to render VocabProvider?',\n );\n }\n if (!context.language) {\n throw new Error(\n 'Attempted to access translation without language set. Did you forget to pass language to VocabProvider?',\n );\n }\n\n return context;\n};\n\nconst SERVER_RENDERING = typeof window === 'undefined';\n\ntype FormatXMLElementReactNodeFn = (parts: ReactNode[]) => ReactNode;\n\ntype MapToReactNodeFunction<Params extends Record<string, any>> = {\n [key in keyof Params]: Params[key] extends ParsedFormatFn\n ? FormatXMLElementReactNodeFn\n : Params[key];\n};\n\ntype TranslateFn<FormatFnByKey extends ParsedFormatFnByKey> = {\n <TranslationKey extends keyof FormatFnByKey>(\n key: TranslationKey,\n params: MapToReactNodeFunction<\n Parameters<FormatFnByKey[TranslationKey]>[0]\n >,\n ): ReturnType<FormatFnByKey[TranslationKey]> extends string\n ? string\n : ReactNode | string | Array<ReactNode | string>;\n <TranslationKey extends keyof FormatFnByKey>(\n key: Parameters<FormatFnByKey[TranslationKey]>[0] extends Record<\n string,\n any\n >\n ? never\n : TranslationKey,\n ): string;\n};\n\nexport function useTranslations<\n Language extends string,\n FormatFnByKey extends ParsedFormatFnByKey,\n>(\n translations: TranslationFile<Language, FormatFnByKey>,\n): {\n ready: boolean;\n t: TranslateFn<FormatFnByKey>;\n} {\n const { language, locale } = useLanguage();\n const [, forceRender] = useReducer((s: number) => s + 1, 0);\n\n const translationsObject = translations.getLoadedMessages(\n language as any,\n locale || language,\n );\n\n let ready = true;\n\n if (!translationsObject) {\n if (SERVER_RENDERING) {\n throw new Error(\n `Translations not synchronously available on server render. Applying translations dynamically server-side is not supported.`,\n );\n }\n\n translations.load(language as any).then(() => {\n forceRender();\n });\n ready = false;\n }\n\n const t = useCallback(\n (key: string, params?: any) => {\n if (!translationsObject) {\n return ' ';\n }\n\n const message = translationsObject?.[key];\n\n if (!message) {\n // eslint-disable-next-line no-console\n console.error(\n `Unable to find translation for key \"${key}\". Possible keys are ${Object.keys(\n translationsObject,\n )\n .map((v) => `\"${v}\"`)\n .join(', ')}`,\n );\n return '';\n }\n\n const result = message.format(params);\n\n if (Array.isArray(result)) {\n for (let i = 0; i < result.length; i++) {\n const item = result[i];\n if (\n typeof item === 'object' &&\n item &&\n !item.key &&\n isValidElement(item)\n ) {\n result[i] = cloneElement(item, { key: `_vocab-${i}` });\n }\n }\n }\n\n return result;\n },\n [translationsObject],\n );\n\n return {\n ready,\n t,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAM,sBAAsBA,cAAM,cAEhC,OAAU;;;;;;;;;;;AAqCZ,MAAa,iBAAiB,EAC5B,UACA,UACA,aACwB;CACxB,MAAM,kCAAuB;EAAE;EAAU;EAAQ,GAAG,CAAC,UAAU,OAAO,CAAC;AAEvE,QACE,4CAAC,oBAAoB,YAAgB,SAClC,SAC4B;;;;;AAOnC,MAAa,oBAA8C;CACzD,MAAM,gCAAqB,oBAAoB;AAC/C,KAAI,CAAC,QACH,OAAM,IAAI,MACR,qGACD;AAEH,KAAI,CAAC,QAAQ,SACX,OAAM,IAAI,MACR,0GACD;AAGH,QAAO;;AAGT,MAAM,mBAAmB,OAAO,WAAW;AA6B3C,SAAgB,gBAId,cAIA;CACA,MAAM,EAAE,UAAU,WAAW,aAAa;CAC1C,MAAM,GAAG,sCAA2B,MAAc,IAAI,GAAG,EAAE;CAE3D,MAAM,qBAAqB,aAAa,kBACtC,UACA,UAAU,SACX;CAED,IAAI,QAAQ;AAEZ,KAAI,CAAC,oBAAoB;AACvB,MAAI,iBACF,OAAM,IAAI,MACR,6HACD;AAGH,eAAa,KAAK,SAAgB,CAAC,WAAW;AAC5C,gBAAa;IACb;AACF,UAAQ;;CAGV,MAAM,4BACH,KAAa,WAAiB;AAC7B,MAAI,CAAC,mBACH,QAAO;EAGT,MAAM,UAAU,qBAAqB;AAErC,MAAI,CAAC,SAAS;AAEZ,WAAQ,MACN,uCAAuC,IAAI,uBAAuB,OAAO,KACvE,mBACD,CACE,KAAK,MAAM,IAAI,EAAE,GAAG,CACpB,KAAK,KAAK,GACd;AACD,UAAO;;EAGT,MAAM,SAAS,QAAQ,OAAO,OAAO;AAErC,MAAI,MAAM,QAAQ,OAAO,CACvB,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;GACtC,MAAM,OAAO,OAAO;AACpB,OACE,OAAO,SAAS,YAChB,QACA,CAAC,KAAK,iCACS,KAAK,CAEpB,QAAO,6BAAkB,MAAM,EAAE,KAAK,UAAU,KAAK,CAAC;;AAK5D,SAAO;IAET,CAAC,mBAAmB,CACrB;AAED,QAAO;EACL;EACA;EACD"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React, { cloneElement, isValidElement, useCallback, useContext, useMemo, useReducer } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components.tsx
|
|
4
|
+
const TranslationsContext = React.createContext(void 0);
|
|
5
|
+
/**
|
|
6
|
+
* Provides a translation context for your application
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { VocabProvider } from '@vocab/react';
|
|
10
|
+
*
|
|
11
|
+
* <VocabProvider language="en">
|
|
12
|
+
* <App />
|
|
13
|
+
* <VocabProvider />
|
|
14
|
+
*/
|
|
15
|
+
const VocabProvider = ({ children, language, locale }) => {
|
|
16
|
+
const value = useMemo(() => ({
|
|
17
|
+
language,
|
|
18
|
+
locale
|
|
19
|
+
}), [language, locale]);
|
|
20
|
+
return /* @__PURE__ */ React.createElement(TranslationsContext.Provider, { value }, children);
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* @returns The `language` and `locale` values passed in to your `VocabProvider`
|
|
24
|
+
*/
|
|
25
|
+
const useLanguage = () => {
|
|
26
|
+
const context = useContext(TranslationsContext);
|
|
27
|
+
if (!context) throw new Error("Attempted to access translation without Vocab context set. Did you forget to render VocabProvider?");
|
|
28
|
+
if (!context.language) throw new Error("Attempted to access translation without language set. Did you forget to pass language to VocabProvider?");
|
|
29
|
+
return context;
|
|
30
|
+
};
|
|
31
|
+
const SERVER_RENDERING = typeof window === "undefined";
|
|
32
|
+
function useTranslations(translations) {
|
|
33
|
+
const { language, locale } = useLanguage();
|
|
34
|
+
const [, forceRender] = useReducer((s) => s + 1, 0);
|
|
35
|
+
const translationsObject = translations.getLoadedMessages(language, locale || language);
|
|
36
|
+
let ready = true;
|
|
37
|
+
if (!translationsObject) {
|
|
38
|
+
if (SERVER_RENDERING) throw new Error(`Translations not synchronously available on server render. Applying translations dynamically server-side is not supported.`);
|
|
39
|
+
translations.load(language).then(() => {
|
|
40
|
+
forceRender();
|
|
41
|
+
});
|
|
42
|
+
ready = false;
|
|
43
|
+
}
|
|
44
|
+
const t = useCallback((key, params) => {
|
|
45
|
+
if (!translationsObject) return " ";
|
|
46
|
+
const message = translationsObject?.[key];
|
|
47
|
+
if (!message) {
|
|
48
|
+
console.error(`Unable to find translation for key "${key}". Possible keys are ${Object.keys(translationsObject).map((v) => `"${v}"`).join(", ")}`);
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
const result = message.format(params);
|
|
52
|
+
if (Array.isArray(result)) for (let i = 0; i < result.length; i++) {
|
|
53
|
+
const item = result[i];
|
|
54
|
+
if (typeof item === "object" && item && !item.key && isValidElement(item)) result[i] = cloneElement(item, { key: `_vocab-${i}` });
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}, [translationsObject]);
|
|
58
|
+
return {
|
|
59
|
+
ready,
|
|
60
|
+
t
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
export { VocabProvider, useLanguage, useTranslations };
|
|
66
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/components.tsx"],"sourcesContent":["import type {\n TranslationFile,\n LanguageName,\n ParsedFormatFnByKey,\n ParsedFormatFn,\n} from '@vocab/core';\n\nimport React, {\n type ReactNode,\n useContext,\n useMemo,\n useReducer,\n isValidElement,\n cloneElement,\n useCallback,\n} from 'react';\n\ntype Locale = string;\n\ninterface TranslationsContextValue {\n /**\n * The `language` passed in to your `VocabProvider`\n */\n language: LanguageName;\n /**\n * The `locale` passed in to your `VocabProvider`\n *\n * Please note that this value will be `undefined` if you have not passed a `locale` to your `VocabProvider`.\n * If your languages are named with IETF language tags, you should just use `language` instead of\n * this value, unless you specifically need to access your `locale` override.\n */\n locale?: Locale;\n}\n\nconst TranslationsContext = React.createContext<\n TranslationsContextValue | undefined\n>(undefined);\n\n// Not extending TranslationsContextValue so we can tailor the docs for each prop to be better\n// suited to the provider, rather than for the useLanguage hook\ninterface VocabProviderProps {\n /**\n * The language to load translations for. Must be one of the language names defined in your `vocab.config.js`.\n */\n language: TranslationsContextValue['language'];\n /**\n * A locale override. By default, Vocab will use the `language` as the locale when formatting messages if\n * `locale` is not set. If your languages are named with IETF language tags, you probably don't need to\n * set this value.\n *\n * You may want to override the locale for a specific language if the default formatting for that locale\n * is not desired.\n *\n * @example\n * // Override the locale for th-TH to use the Gregorian calendar instead of the default Buddhist calendar\n * <VocabProvider language=\"th-TH\" locale=\"th-TH-u-ca-gregory\">\n * </App>\n * <VocabProvider />\n */\n locale?: TranslationsContextValue['locale'];\n children: ReactNode;\n}\n\n/**\n * Provides a translation context for your application\n *\n * @example\n * import { VocabProvider } from '@vocab/react';\n *\n * <VocabProvider language=\"en\">\n * <App />\n * <VocabProvider />\n */\nexport const VocabProvider = ({\n children,\n language,\n locale,\n}: VocabProviderProps) => {\n const value = useMemo(() => ({ language, locale }), [language, locale]);\n\n return (\n <TranslationsContext.Provider value={value}>\n {children}\n </TranslationsContext.Provider>\n );\n};\n\n/**\n * @returns The `language` and `locale` values passed in to your `VocabProvider`\n */\nexport const useLanguage = (): TranslationsContextValue => {\n const context = useContext(TranslationsContext);\n if (!context) {\n throw new Error(\n 'Attempted to access translation without Vocab context set. Did you forget to render VocabProvider?',\n );\n }\n if (!context.language) {\n throw new Error(\n 'Attempted to access translation without language set. Did you forget to pass language to VocabProvider?',\n );\n }\n\n return context;\n};\n\nconst SERVER_RENDERING = typeof window === 'undefined';\n\ntype FormatXMLElementReactNodeFn = (parts: ReactNode[]) => ReactNode;\n\ntype MapToReactNodeFunction<Params extends Record<string, any>> = {\n [key in keyof Params]: Params[key] extends ParsedFormatFn\n ? FormatXMLElementReactNodeFn\n : Params[key];\n};\n\ntype TranslateFn<FormatFnByKey extends ParsedFormatFnByKey> = {\n <TranslationKey extends keyof FormatFnByKey>(\n key: TranslationKey,\n params: MapToReactNodeFunction<\n Parameters<FormatFnByKey[TranslationKey]>[0]\n >,\n ): ReturnType<FormatFnByKey[TranslationKey]> extends string\n ? string\n : ReactNode | string | Array<ReactNode | string>;\n <TranslationKey extends keyof FormatFnByKey>(\n key: Parameters<FormatFnByKey[TranslationKey]>[0] extends Record<\n string,\n any\n >\n ? never\n : TranslationKey,\n ): string;\n};\n\nexport function useTranslations<\n Language extends string,\n FormatFnByKey extends ParsedFormatFnByKey,\n>(\n translations: TranslationFile<Language, FormatFnByKey>,\n): {\n ready: boolean;\n t: TranslateFn<FormatFnByKey>;\n} {\n const { language, locale } = useLanguage();\n const [, forceRender] = useReducer((s: number) => s + 1, 0);\n\n const translationsObject = translations.getLoadedMessages(\n language as any,\n locale || language,\n );\n\n let ready = true;\n\n if (!translationsObject) {\n if (SERVER_RENDERING) {\n throw new Error(\n `Translations not synchronously available on server render. Applying translations dynamically server-side is not supported.`,\n );\n }\n\n translations.load(language as any).then(() => {\n forceRender();\n });\n ready = false;\n }\n\n const t = useCallback(\n (key: string, params?: any) => {\n if (!translationsObject) {\n return ' ';\n }\n\n const message = translationsObject?.[key];\n\n if (!message) {\n // eslint-disable-next-line no-console\n console.error(\n `Unable to find translation for key \"${key}\". Possible keys are ${Object.keys(\n translationsObject,\n )\n .map((v) => `\"${v}\"`)\n .join(', ')}`,\n );\n return '';\n }\n\n const result = message.format(params);\n\n if (Array.isArray(result)) {\n for (let i = 0; i < result.length; i++) {\n const item = result[i];\n if (\n typeof item === 'object' &&\n item &&\n !item.key &&\n isValidElement(item)\n ) {\n result[i] = cloneElement(item, { key: `_vocab-${i}` });\n }\n }\n }\n\n return result;\n },\n [translationsObject],\n );\n\n return {\n ready,\n t,\n };\n}\n"],"mappings":";;;AAkCA,MAAM,sBAAsB,MAAM,cAEhC,OAAU;;;;;;;;;;;AAqCZ,MAAa,iBAAiB,EAC5B,UACA,UACA,aACwB;CACxB,MAAM,QAAQ,eAAe;EAAE;EAAU;EAAQ,GAAG,CAAC,UAAU,OAAO,CAAC;AAEvE,QACE,oCAAC,oBAAoB,YAAgB,SAClC,SAC4B;;;;;AAOnC,MAAa,oBAA8C;CACzD,MAAM,UAAU,WAAW,oBAAoB;AAC/C,KAAI,CAAC,QACH,OAAM,IAAI,MACR,qGACD;AAEH,KAAI,CAAC,QAAQ,SACX,OAAM,IAAI,MACR,0GACD;AAGH,QAAO;;AAGT,MAAM,mBAAmB,OAAO,WAAW;AA6B3C,SAAgB,gBAId,cAIA;CACA,MAAM,EAAE,UAAU,WAAW,aAAa;CAC1C,MAAM,GAAG,eAAe,YAAY,MAAc,IAAI,GAAG,EAAE;CAE3D,MAAM,qBAAqB,aAAa,kBACtC,UACA,UAAU,SACX;CAED,IAAI,QAAQ;AAEZ,KAAI,CAAC,oBAAoB;AACvB,MAAI,iBACF,OAAM,IAAI,MACR,6HACD;AAGH,eAAa,KAAK,SAAgB,CAAC,WAAW;AAC5C,gBAAa;IACb;AACF,UAAQ;;CAGV,MAAM,IAAI,aACP,KAAa,WAAiB;AAC7B,MAAI,CAAC,mBACH,QAAO;EAGT,MAAM,UAAU,qBAAqB;AAErC,MAAI,CAAC,SAAS;AAEZ,WAAQ,MACN,uCAAuC,IAAI,uBAAuB,OAAO,KACvE,mBACD,CACE,KAAK,MAAM,IAAI,EAAE,GAAG,CACpB,KAAK,KAAK,GACd;AACD,UAAO;;EAGT,MAAM,SAAS,QAAQ,OAAO,OAAO;AAErC,MAAI,MAAM,QAAQ,OAAO,CACvB,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;GACtC,MAAM,OAAO,OAAO;AACpB,OACE,OAAO,SAAS,YAChB,QACA,CAAC,KAAK,OACN,eAAe,KAAK,CAEpB,QAAO,KAAK,aAAa,MAAM,EAAE,KAAK,UAAU,KAAK,CAAC;;AAK5D,SAAO;IAET,CAAC,mBAAmB,CACrB;AAED,QAAO;EACL;EACA;EACD"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vocab/react",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14-fix-messageformat-import-20250923014407",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/seek-oss/vocab.git",
|
|
7
7
|
"directory": "packages/react"
|
|
8
8
|
},
|
|
9
|
-
"main": "dist/
|
|
10
|
-
"module": "dist/
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
11
19
|
"author": "SEEK",
|
|
12
20
|
"license": "MIT",
|
|
13
21
|
"peerDependencies": {
|
|
@@ -18,10 +26,10 @@
|
|
|
18
26
|
],
|
|
19
27
|
"dependencies": {
|
|
20
28
|
"intl-messageformat": "^10.0.0",
|
|
21
|
-
"@vocab/core": "^1.6.
|
|
29
|
+
"@vocab/core": "^1.6.5-fix-messageformat-import-20250923014407"
|
|
22
30
|
},
|
|
23
31
|
"devDependencies": {
|
|
24
|
-
"@types/react": "^
|
|
25
|
-
"react": "^
|
|
32
|
+
"@types/react": "^19.1.8",
|
|
33
|
+
"react": "^19.1.0"
|
|
26
34
|
}
|
|
27
35
|
}
|