val-i18n-react
Version:
React goodies for val-i18n
1 lines • 5.03 kB
Source Map (JSON)
{"version":3,"sources":["../src/hooks.ts","../src/i18n-context.ts","../src/trans.ts"],"names":["createElement","slices"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,cAAc;;;ACEvB,SAAS,eAAe,qBAAqB;AAEtC,IAAM,cAA8B,8BAA2B,IAAI;AAenE,IAAM,eAAyD,CAAC;AAAA,EACrE;AAAA,EACA;AACF,MAAM,cAAc,YAAY,UAAU,EAAE,OAAO,MAAM,SAAS,CAAC;;;ADd5D,IAAM,UAAU,MAAY;AACjC,QAAM,OAAO,WAAW,WAAW;AACnC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,uBAAuB;AAAA,EACzC;AACA,SAAO;AACT;AAKO,IAAM,eAAe,MAAiB,OAAO,QAAQ,EAAE,EAAE;AAKzD,IAAM,UAAU,MAAkB,OAAO,QAAQ,EAAE,KAAK;;;AExB/D,SAAS,gBAAgB,SAAS,iBAAAA,gBAAe,gBAAgB;AAuC1D,IAAM,QAAuC,CAAC,EAAE,SAAS,SAAS,MAAM;AAC7E,MAAI,CAAC;AAAS,WAAO;AAErB,QAAM,SAAS,QAAQ,MAAM;AAC3B,UAAMC,UAAmB,CAAC;AAC1B,UAAM,YAAY;AAClB,QAAI;AACJ,QAAI,UAAU;AACd,WAAQ,QAAQ,UAAU,KAAK,OAAO,GAAI;AACxC,MAAAA,QAAO,KAAK,QAAQ,MAAM,SAAS,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC;AACzD,gBAAU,MAAM,QAAQ,MAAM,CAAC,EAAE;AAAA,IACnC;AACA,IAAAA,QAAO,KAAK,QAAQ,MAAM,OAAO,CAAC;AAClC,WAAOA;AAAA,EACT,GAAG,CAAC,OAAO,CAAC;AAGZ,MAAI,OAAO,WAAW;AAAG,WAAOD,eAAc,UAAU,MAAM,OAAO;AAErE,QAAM,QAAmC,CAAC;AAC1C,MAAI,UAAU;AAEd,aAAW,SAAS,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,GAAG;AACnE,QAAI,eAAe,KAAK,GAAG;AACzB,YAAM,MAAO,MAAM,MAAc,aAAa;AAC9C,UAAI,KAAK;AACP,cAAM,GAAG,IAAI;AACb,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAEA,QAAM,OAAoB,OAAO,MAAM;AACvC,MAAI,SAAS;AACX,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,WAAK,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC;AAAA,IAC7C;AAAA,EACF,OAAO;AACL,SAAK,CAAC,IAAI,YAAY,KAAK,OAAO,CAAC;AAAA,EACrC;AACA,SAAOA,eAAc,UAAU,MAAM,IAAI;AAC3C","sourcesContent":["import { useContext } from \"react\";\nimport { useVal } from \"use-value-enhancer\";\nimport { I18nContext } from \"./i18n-context\";\n\nimport type { I18n, LocaleLang, TFunction } from \"val-i18n\";\n\n/**\n * @returns The {@link I18n} instance from the nearest {@link I18nProvider}. Throws if not found.\n */\nexport const useI18n = (): I18n => {\n const i18n = useContext(I18nContext);\n if (!i18n) {\n throw new Error(\"I18nContext not found\");\n }\n return i18n;\n};\n\n/**\n * @returns The {@link TFunction} from the nearest {@link I18nProvider}. Throws if not found.\n */\nexport const useTranslate = (): TFunction => useVal(useI18n().t$);\n\n/**\n * @returns The {@link LocaleLang} from the nearest {@link I18nProvider}. Throws if not found.\n */\nexport const useLang = (): LocaleLang => useVal(useI18n().lang$);\n","import type { FC, PropsWithChildren } from \"react\";\nimport type { I18n } from \"val-i18n\";\n\nimport { createContext, createElement } from \"react\";\n\nexport const I18nContext = /* @__PURE__ */ createContext<I18n | null>(null);\n\nexport interface I18nProviderProps {\n i18n: I18n;\n}\n\n/**\n * Provides the I18n instance to the rest of the app.\n *\n * @example\n * ```tsx\n * <I18nProvider i18n={i18n}>\n * <App />\n * </I18nProvider>\n */\nexport const I18nProvider: FC<PropsWithChildren<I18nProviderProps>> = ({\n i18n,\n children,\n}) => createElement(I18nContext.Provider, { value: i18n, children });\n","import type { FC, PropsWithChildren, ReactNode } from \"react\";\nimport { isValidElement, useMemo, createElement, Fragment } from \"react\";\n\nexport interface TProps {\n /** Locale translation message */\n message: string;\n}\n\n/**\n * Insert React elements to the translation message.\n *\n * @example\n * ```jsx\n * <Trans message=\"a{{b}}c{{d}}e\">\n * <h1 data-t-slot=\"b\">B</h1>\n * <p data-t-slot=\"d\">D</p>\n * </Trans>\n * ```\n * ↓\n * ```jsx\n * <>\n * a<h1 data-t-slot=\"b\">B</h1>c<p data-t-slot=\"d\">D</p>e\n * <>\n * ```\n *\n * `data-t-slot` can be ignored if there is only one placeholder.\n *\n * @example\n * ```jsx\n * <Trans message=\"a{{b}}c\">\n * <h1>B</h1>\n * </Trans>\n * ```\n * ↓\n * ```jsx\n * <>\n * a<h1>B</h1>c\n * </>\n * ```\n */\nexport const Trans: FC<PropsWithChildren<TProps>> = ({ message, children }) => {\n if (!message) return null;\n\n const slices = useMemo(() => {\n const slices: string[] = [];\n const matchArgs = /{{(\\S+?)}}/gi;\n let slice: RegExpExecArray | null;\n let pointer = 0;\n while ((slice = matchArgs.exec(message))) {\n slices.push(message.slice(pointer, slice.index), slice[1]);\n pointer = slice.index + slice[0].length;\n }\n slices.push(message.slice(pointer));\n return slices;\n }, [message]);\n\n // no template\n if (slices.length === 1) return createElement(Fragment, null, message);\n\n const slots: Record<string, ReactNode> = {};\n let hasSlot = false;\n\n for (const child of Array.isArray(children) ? children : [children]) {\n if (isValidElement(child)) {\n const key = (child.props as any)[\"data-t-slot\"];\n if (key) {\n slots[key] = child;\n hasSlot = true;\n }\n }\n }\n\n const copy: ReactNode[] = slices.slice();\n if (hasSlot) {\n for (let i = 1; i < copy.length; i += 2) {\n copy[i] = slots[slices[i]] || `{{${slices[i]}}}`;\n }\n } else {\n copy[1] = children || `{{${slices[1]}}}`;\n }\n return createElement(Fragment, null, copy);\n};\n"]}