diff --git a/TransWithoutContext.d.ts b/TransWithoutContext.d.ts new file mode 100644 index 00000000..de326ae5 --- /dev/null +++ b/TransWithoutContext.d.ts @@ -0,0 +1,33 @@ +import { i18n, TFuncKey, Namespace, TypeOptions, TFunction, KeyPrefix } from 'i18next'; +import * as React from 'react'; + +type DefaultNamespace = TypeOptions['defaultNS']; + +type TransChild = React.ReactNode | Record; +export type TransProps< + K extends TFuncKey extends infer A ? A : never, + N extends Namespace = DefaultNamespace, + TKPrefix = undefined, + E = React.HTMLProps +> = E & { + children?: TransChild | TransChild[]; + components?: readonly React.ReactElement[] | { readonly [tagName: string]: React.ReactElement }; + count?: number; + context?: string; + defaults?: string; + i18n?: i18n; + i18nKey?: K | K[]; + ns?: N; + parent?: string | React.ComponentType | null; // used in React.createElement if not null + tOptions?: {}; + values?: {}; + shouldUnescape?: boolean; + t?: TFunction; +}; + +export function Trans< + K extends TFuncKey extends infer A ? A : never, + N extends Namespace = DefaultNamespace, + TKPrefix extends KeyPrefix = undefined, + E = React.HTMLProps +>(props: TransProps): React.ReactElement; diff --git a/index.d.ts b/index.d.ts index a2c5d1c2..44d6bfcc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,15 +1,15 @@ import i18next, { ReactOptions, i18n, - ThirdPartyModule, Resource, - TFuncKey, Namespace, TypeOptions, TFunction, KeyPrefix, } from 'i18next'; import * as React from 'react'; +export { Trans, TransProps } from './TransWithoutContext'; +export { initReactI18next } from './initReactI18next'; type Subtract = Omit; @@ -17,7 +17,6 @@ export function setDefaults(options: ReactOptions): void; export function getDefaults(): ReactOptions; export function setI18n(instance: i18n): void; export function getI18n(): i18n; -export const initReactI18next: ThirdPartyModule; export function composeInitialProps(ForComponent: any): (ctx: unknown) => Promise; export function getInitialProps(): { initialI18nStore: { @@ -50,35 +49,6 @@ declare module 'react' { type DefaultNamespace = TypeOptions['defaultNS']; -type TransChild = React.ReactNode | Record; -export type TransProps< - K extends TFuncKey extends infer A ? A : never, - N extends Namespace = DefaultNamespace, - TKPrefix = undefined, - E = React.HTMLProps -> = E & { - children?: TransChild | TransChild[]; - components?: readonly React.ReactElement[] | { readonly [tagName: string]: React.ReactElement }; - count?: number; - context?: string; - defaults?: string; - i18n?: i18n; - i18nKey?: K | K[]; - ns?: N; - parent?: string | React.ComponentType | null; // used in React.createElement if not null - tOptions?: {}; - values?: {}; - shouldUnescape?: boolean; - t?: TFunction; -}; - -export function Trans< - K extends TFuncKey extends infer A ? A : never, - N extends Namespace = DefaultNamespace, - TKPrefix extends KeyPrefix = undefined, - E = React.HTMLProps ->(props: TransProps): React.ReactElement; - export function useSSR(initialI18nStore: Resource, initialLanguage: string): void; export interface UseTranslationOptions { diff --git a/initReactI18next.d.ts b/initReactI18next.d.ts new file mode 100644 index 00000000..57ba6927 --- /dev/null +++ b/initReactI18next.d.ts @@ -0,0 +1,3 @@ +import { ThirdPartyModule } from 'i18next'; + +export const initReactI18next: ThirdPartyModule; diff --git a/package.json b/package.json index 39ab83e1..02878c42 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,24 @@ "react", "reactjs" ], + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./index.d.ts", + "import": "./dist/es/index.js", + "require": "./dist/commonjs/index.js" + }, + "./TransWithoutContext": { + "types": "./TransWithoutContext.d.ts", + "import": "./dist/es/TransWithoutContext.js", + "require": "./dist/commonjs/TransWithoutContext.js" + }, + "./initReactI18next": { + "types": "./initReactI18next.d.ts", + "import": "./dist/es/initReactI18next.js", + "require": "./dist/commonjs/initReactI18next.js" + } + }, "homepage": "https://github.com/i18next/react-i18next", "bugs": "https://github.com/i18next/react-i18next/issues", "repository": { diff --git a/react-i18next.js b/react-i18next.js index e35ce30b..3d05e1b3 100644 --- a/react-i18next.js +++ b/react-i18next.js @@ -318,126 +318,6 @@ } }; - var matchHtmlEntity = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g; - var htmlEntities = { - '&': '&', - '&': '&', - '<': '<', - '<': '<', - '>': '>', - '>': '>', - ''': "'", - ''': "'", - '"': '"', - '"': '"', - ' ': ' ', - ' ': ' ', - '©': '©', - '©': '©', - '®': '®', - '®': '®', - '…': '…', - '…': '…', - '/': '/', - '/': '/' - }; - - var unescapeHtmlEntity = function unescapeHtmlEntity(m) { - return htmlEntities[m]; - }; - - var unescape = function unescape(text) { - return text.replace(matchHtmlEntity, unescapeHtmlEntity); - }; - - var defaultOptions = { - bindI18n: 'languageChanged', - bindI18nStore: '', - transEmptyNodeValue: '', - transSupportBasicHtmlNodes: true, - transWrapTextNodes: '', - transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'], - useSuspense: true, - unescape: unescape - }; - var i18nInstance; - var I18nContext = react.createContext(); - function setDefaults() { - var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - defaultOptions = _objectSpread2(_objectSpread2({}, defaultOptions), options); - } - function getDefaults() { - return defaultOptions; - } - var ReportNamespaces = function () { - function ReportNamespaces() { - _classCallCheck(this, ReportNamespaces); - - this.usedNamespaces = {}; - } - - _createClass(ReportNamespaces, [{ - key: "addUsedNamespaces", - value: function addUsedNamespaces(namespaces) { - var _this = this; - - namespaces.forEach(function (ns) { - if (!_this.usedNamespaces[ns]) _this.usedNamespaces[ns] = true; - }); - } - }, { - key: "getUsedNamespaces", - value: function getUsedNamespaces() { - return Object.keys(this.usedNamespaces); - } - }]); - - return ReportNamespaces; - }(); - function setI18n(instance) { - i18nInstance = instance; - } - function getI18n() { - return i18nInstance; - } - var initReactI18next = { - type: '3rdParty', - init: function init(instance) { - setDefaults(instance.options.react); - setI18n(instance); - } - }; - function composeInitialProps(ForComponent) { - return function (ctx) { - return new Promise(function (resolve) { - var i18nInitialProps = getInitialProps(); - - if (ForComponent.getInitialProps) { - ForComponent.getInitialProps(ctx).then(function (componentsInitialProps) { - resolve(_objectSpread2(_objectSpread2({}, componentsInitialProps), i18nInitialProps)); - }); - } else { - resolve(i18nInitialProps); - } - }); - }; - } - function getInitialProps() { - var i18n = getI18n(); - var namespaces = i18n.reportNamespaces ? i18n.reportNamespaces.getUsedNamespaces() : []; - var ret = {}; - var initialI18nStore = {}; - i18n.languages.forEach(function (l) { - initialI18nStore[l] = {}; - namespaces.forEach(function (ns) { - initialI18nStore[l][ns] = i18n.getResourceBundle(l, ns) || {}; - }); - }); - ret.initialI18nStore = initialI18nStore; - ret.initialLanguage = i18n.language; - return ret; - } - function warn() { if (console && console.warn) { var _console; @@ -521,6 +401,64 @@ return Component.displayName || Component.name || (typeof Component === 'string' && Component.length > 0 ? Component : 'Unknown'); } + var matchHtmlEntity = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g; + var htmlEntities = { + '&': '&', + '&': '&', + '<': '<', + '<': '<', + '>': '>', + '>': '>', + ''': "'", + ''': "'", + '"': '"', + '"': '"', + ' ': ' ', + ' ': ' ', + '©': '©', + '©': '©', + '®': '®', + '®': '®', + '…': '…', + '…': '…', + '/': '/', + '/': '/' + }; + + var unescapeHtmlEntity = function unescapeHtmlEntity(m) { + return htmlEntities[m]; + }; + + var unescape = function unescape(text) { + return text.replace(matchHtmlEntity, unescapeHtmlEntity); + }; + + var defaultOptions = { + bindI18n: 'languageChanged', + bindI18nStore: '', + transEmptyNodeValue: '', + transSupportBasicHtmlNodes: true, + transWrapTextNodes: '', + transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'], + useSuspense: true, + unescape: unescape + }; + function setDefaults() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + defaultOptions = _objectSpread2(_objectSpread2({}, defaultOptions), options); + } + function getDefaults() { + return defaultOptions; + } + + var i18nInstance; + function setI18n(instance) { + i18nInstance = instance; + } + function getI18n() { + return i18nInstance; + } + var _excluded = ["format"], _excluded2 = ["children", "count", "parent", "i18nKey", "context", "tOptions", "values", "defaults", "components", "ns", "i18n", "t", "shouldUnescape"]; @@ -741,11 +679,7 @@ shouldUnescape = _ref.shouldUnescape, additionalProps = _objectWithoutProperties(_ref, _excluded2); - var _ref2 = react.useContext(I18nContext) || {}, - i18nFromContext = _ref2.i18n, - defaultNSFromContext = _ref2.defaultNS; - - var i18n = i18nFromProps || i18nFromContext || getI18n(); + var i18n = i18nFromProps || getI18n(); if (!i18n) { warnOnce('You will need to pass in an i18next instance by using i18nextReactModule'); @@ -760,7 +694,7 @@ var reactI18nextOptions = _objectSpread2(_objectSpread2({}, getDefaults()), i18n.options && i18n.options.react); - var namespaces = ns || t.ns || defaultNSFromContext || i18n.options && i18n.options.defaultNS; + var namespaces = ns || t.ns || i18n.options && i18n.options.defaultNS; namespaces = typeof namespaces === 'string' ? [namespaces] : namespaces || ['translation']; var defaultValue = defaults || nodesToString(children, reactI18nextOptions) || reactI18nextOptions.transEmptyNodeValue || i18nKey; var hashTransKey = reactI18nextOptions.hashTransKey; @@ -785,6 +719,116 @@ return useAsParent ? react.createElement(useAsParent, additionalProps, content) : content; } + var initReactI18next = { + type: '3rdParty', + init: function init(instance) { + setDefaults(instance.options.react); + setI18n(instance); + } + }; + + var I18nContext = react.createContext(); + var ReportNamespaces = function () { + function ReportNamespaces() { + _classCallCheck(this, ReportNamespaces); + + this.usedNamespaces = {}; + } + + _createClass(ReportNamespaces, [{ + key: "addUsedNamespaces", + value: function addUsedNamespaces(namespaces) { + var _this = this; + + namespaces.forEach(function (ns) { + if (!_this.usedNamespaces[ns]) _this.usedNamespaces[ns] = true; + }); + } + }, { + key: "getUsedNamespaces", + value: function getUsedNamespaces() { + return Object.keys(this.usedNamespaces); + } + }]); + + return ReportNamespaces; + }(); + function composeInitialProps(ForComponent) { + return function (ctx) { + return new Promise(function (resolve) { + var i18nInitialProps = getInitialProps(); + + if (ForComponent.getInitialProps) { + ForComponent.getInitialProps(ctx).then(function (componentsInitialProps) { + resolve(_objectSpread2(_objectSpread2({}, componentsInitialProps), i18nInitialProps)); + }); + } else { + resolve(i18nInitialProps); + } + }); + }; + } + function getInitialProps() { + var i18n = getI18n(); + var namespaces = i18n.reportNamespaces ? i18n.reportNamespaces.getUsedNamespaces() : []; + var ret = {}; + var initialI18nStore = {}; + i18n.languages.forEach(function (l) { + initialI18nStore[l] = {}; + namespaces.forEach(function (ns) { + initialI18nStore[l][ns] = i18n.getResourceBundle(l, ns) || {}; + }); + }); + ret.initialI18nStore = initialI18nStore; + ret.initialLanguage = i18n.language; + return ret; + } + + var _excluded$1 = ["children", "count", "parent", "i18nKey", "context", "tOptions", "values", "defaults", "components", "ns", "i18n", "t", "shouldUnescape"]; + function Trans$1(_ref) { + var children = _ref.children, + count = _ref.count, + parent = _ref.parent, + i18nKey = _ref.i18nKey, + context = _ref.context, + _ref$tOptions = _ref.tOptions, + tOptions = _ref$tOptions === void 0 ? {} : _ref$tOptions, + values = _ref.values, + defaults = _ref.defaults, + components = _ref.components, + ns = _ref.ns, + i18nFromProps = _ref.i18n, + tFromProps = _ref.t, + shouldUnescape = _ref.shouldUnescape, + additionalProps = _objectWithoutProperties(_ref, _excluded$1); + + var _ref2 = react.useContext(I18nContext) || {}, + i18nFromContext = _ref2.i18n, + defaultNSFromContext = _ref2.defaultNS; + + var i18n = i18nFromProps || i18nFromContext || getI18n(); + + var t = tFromProps || i18n.t.bind(i18n) || function (k) { + return k; + }; + + return Trans(_objectSpread2({ + children: children, + count: count, + parent: parent, + i18nKey: i18nKey, + context: context, + tOptions: tOptions, + values: values, + defaults: defaults, + components: components, + ns: ns || t.ns || defaultNSFromContext || i18n.options && i18n.options.defaultNS, + i18n: i18n, + t: tFromProps, + shouldUnescape: shouldUnescape + }, additionalProps)); + } + var usePrevious = function usePrevious(value, ignore) { var ref = react.useRef(); react.useEffect(function () { @@ -895,13 +939,13 @@ }); } - var _excluded$1 = ["forwardedRef"]; + var _excluded$2 = ["forwardedRef"]; function withTranslation(ns) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return function Extend(WrappedComponent) { function I18nextWithTranslation(_ref) { var forwardedRef = _ref.forwardedRef, - rest = _objectWithoutProperties(_ref, _excluded$1); + rest = _objectWithoutProperties(_ref, _excluded$2); var _useTranslation = useTranslation(ns, _objectSpread2(_objectSpread2({}, rest), {}, { keyPrefix: options.keyPrefix @@ -939,11 +983,11 @@ }; } - var _excluded$2 = ["ns", "children"]; + var _excluded$3 = ["ns", "children"]; function Translation(props) { var ns = props.ns, children = props.children, - options = _objectWithoutProperties(props, _excluded$2); + options = _objectWithoutProperties(props, _excluded$3); var _useTranslation = useTranslation(ns, options), _useTranslation2 = _slicedToArray(_useTranslation, 3), @@ -1000,13 +1044,13 @@ } } - var _excluded$3 = ["initialI18nStore", "initialLanguage"]; + var _excluded$4 = ["initialI18nStore", "initialLanguage"]; function withSSR() { return function Extend(WrappedComponent) { function I18nextWithSSR(_ref) { var initialI18nStore = _ref.initialI18nStore, initialLanguage = _ref.initialLanguage, - rest = _objectWithoutProperties(_ref, _excluded$3); + rest = _objectWithoutProperties(_ref, _excluded$4); useSSR(initialI18nStore, initialLanguage); return react.createElement(WrappedComponent, _objectSpread2({}, rest)); @@ -1040,7 +1084,8 @@ exports.I18nContext = I18nContext; exports.I18nextProvider = I18nextProvider; - exports.Trans = Trans; + exports.Trans = Trans$1; + exports.TransWithoutContext = Trans; exports.Translation = Translation; exports.composeInitialProps = composeInitialProps; exports.date = date; diff --git a/react-i18next.min.js b/react-i18next.min.js index 31e96880..2f59b103 100644 --- a/react-i18next.min.js +++ b/react-i18next.min.js @@ -1 +1 @@ -!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],n):n((e=e||self).ReactI18next={},e.React)}(this,(function(e,n){"use strict";function t(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function r(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}function s(e,n){return function(e){if(Array.isArray(e))return e}(e)||function(e,n){var t=e&&("undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"]);if(null==t)return;var r,a,i=[],o=!0,c=!1;try{for(t=t.call(e);!(o=(r=t.next()).done)&&(i.push(r.value),!n||i.length!==n);o=!0);}catch(e){c=!0,a=e}finally{try{o||null==t.return||t.return()}finally{if(c)throw a}}return i}(e,n)||function(e,n){if(!e)return;if("string"==typeof e)return u(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);"Object"===t&&e.constructor&&(t=e.constructor.name);if("Map"===t||"Set"===t)return Array.from(e);if("Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t))return u(e,n)}(e,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,r=new Array(n);t<]+?)[\s/>]|([^\s=]+)=\s?(".*?"|'.*?')/g;function p(e){var n={type:"tag",name:"",voidElement:!1,attrs:{},children:[]},t=e.match(/<\/?([^\s]+?)[/\s>]/);if(t&&(n.name=t[1],(l[t[1]]||"/"===e.charAt(e.length-2))&&(n.voidElement=!0),n.name.startsWith("!--"))){var r=e.indexOf("--\x3e");return{type:"comment",comment:-1!==r?e.slice(4,r):""}}for(var a=new RegExp(f),i=null;null!==(i=a.exec(e));)if(i[0].trim())if(i[1]){var o=i[1].trim(),c=[o,""];o.indexOf("=")>-1&&(c=o.split("=")),n.attrs[c[0]]=c[1],a.lastIndex--}else i[2]&&(n.attrs[i[2]]=i[3].trim().substring(1,i[3].length-1));return n}var d=/<[a-zA-Z0-9\-\!\/](?:"[^"]*"|'[^']*'|[^'">])*>/g,g=/^\s*$/,m=Object.create(null);var h,v=function(e,n){n||(n={}),n.components||(n.components=m);var t,r=[],a=[],i=-1,o=!1;if(0!==e.indexOf("<")){var c=e.indexOf("<");r.push({type:"text",content:-1===c?e:e.substring(0,c)})}return e.replace(d,(function(c,s){if(o){if(c!=="")return;o=!1}var u,l="/"!==c.charAt(1),f=c.startsWith("\x3c!--"),d=s+c.length,m=e.charAt(d);if(f){var h=p(c);return i<0?(r.push(h),r):((u=a[i]).children.push(h),r)}if(l&&(i++,"tag"===(t=p(c)).type&&n.components[t.name]&&(t.type="component",o=!0),t.voidElement||o||!m||"<"===m||t.children.push({type:"text",content:e.slice(d,e.indexOf("<",d))}),0===i&&r.push(t),(u=a[i-1])&&u.children.push(t),a[i]=t),(!l||t.voidElement)&&(i>-1&&(t.voidElement||t.name===c.slice(2,-1))&&(i--,t=-1===i?r:a[i]),!o&&"<"!==m&&m)){u=-1===i?r:a[i].children;var v=e.indexOf("<",d),y=e.slice(d,-1===v?void 0:v);g.test(y)&&(y=" "),(v>-1&&i+u.length>=0||" "!==y)&&u.push({type:"text",content:y})}})),r},y=/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g,b={"&":"&","&":"&","<":"<","<":"<",">":">",">":">","'":"'","'":"'",""":'"',""":'"'," ":" "," ":" ","©":"©","©":"©","®":"®","®":"®","…":"…","…":"…","/":"/","/":"/"},O=function(e){return b[e]},x={bindI18n:"languageChanged",bindI18nStore:"",transEmptyNodeValue:"",transSupportBasicHtmlNodes:!0,transWrapTextNodes:"",transKeepBasicHtmlNodesFor:["br","strong","i","p"],useSuspense:!0,unescape:function(e){return e.replace(y,O)}},w=n.createContext();function j(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};x=r(r({},x),e)}function E(){return x}var S=function(){function e(){!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,e),this.usedNamespaces={}}var n,t,r;return n=e,(t=[{key:"addUsedNamespaces",value:function(e){var n=this;e.forEach((function(e){n.usedNamespaces[e]||(n.usedNamespaces[e]=!0)}))}},{key:"getUsedNamespaces",value:function(){return Object.keys(this.usedNamespaces)}}])&&i(n.prototype,t),r&&i(n,r),e}();function N(e){h=e}function k(){return h}var I={type:"3rdParty",init:function(e){j(e.options.react),N(e)}};function P(e){return function(n){return new Promise((function(t){var a=R();e.getInitialProps?e.getInitialProps(n).then((function(e){t(r(r({},e),a))})):t(a)}))}}function R(){var e=k(),n=e.reportNamespaces?e.reportNamespaces.getUsedNamespaces():[],t={},r={};return e.languages.forEach((function(t){r[t]={},n.forEach((function(n){r[t][n]=e.getResourceBundle(t,n)||{}}))})),t.initialI18nStore=r,t.initialLanguage=e.language,t}function C(){if(console&&console.warn){for(var e,n=arguments.length,t=new Array(n),r=0;r2&&void 0!==arguments[2]?arguments[2]:{},r=n.languages[0],a=!!n.options&&n.options.fallbackLng,i=n.languages[n.languages.length-1];if("cimode"===r.toLowerCase())return!0;var o=function(e,t){var r=n.services.backendConnector.state["".concat(e,"|").concat(t)];return-1===r||2===r};return!(t.bindI18n&&t.bindI18n.indexOf("languageChanging")>-1&&n.services.backendConnector.backend&&n.isLanguageChangingTo&&!o(n.isLanguageChangingTo,e))&&(!!n.hasResourceBundle(r,e)||(!(n.services.backendConnector.backend&&(!n.options.resources||n.options.partialBundledLanguages))||!(!o(r,e)||a&&!o(i,e))))}function B(e,n){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(!n.languages||!n.languages.length)return A("i18n.languages were undefined or empty",n.languages),!0;var r=void 0!==n.options.ignoreJSONStructure;return r?n.hasLoadedNamespace(e,{precheck:function(n,r){if(t.bindI18n&&t.bindI18n.indexOf("languageChanging")>-1&&n.services.backendConnector.backend&&n.isLanguageChangingTo&&!r(n.isLanguageChangingTo,e))return!1}}):z(e,n,t)}function U(e){return e.displayName||e.name||("string"==typeof e&&e.length>0?e:"Unknown")}var D=["format"],F=["children","count","parent","i18nKey","context","tOptions","values","defaults","components","ns","i18n","t","shouldUnescape"];function H(e,n){if(!e)return!1;var t=e.props?e.props.children:e.children;return n?t.length>0:!!t}function K(e){return e?e.props?e.props.children:e.children:[]}function V(e){return Array.isArray(e)?e:[e]}function W(e,t,i,o,c,s){if(""===t)return[];var u=o.transKeepBasicHtmlNodesFor||[],l=t&&new RegExp(u.join("|")).test(t);if(!e&&!l)return[t];var f={};!function e(t){V(t).forEach((function(t){"string"!=typeof t&&(H(t)?e(K(t)):"object"!==a(t)||n.isValidElement(t)||Object.assign(f,t))}))}(e);var p=v("<0>".concat(t,"")),d=r(r({},f),c);function g(e,t,r){var a=K(e),i=h(a,t.children,r);return function(e){return"[object Array]"===Object.prototype.toString.call(e)&&e.every((function(e){return n.isValidElement(e)}))}(a)&&0===i.length?a:i}function m(e,t,a,i,o){e.dummy&&(e.children=t),a.push(n.cloneElement(e,r(r({},e.props),{},{key:i}),o?void 0:t))}function h(t,c,f){var p=V(t);return V(c).reduce((function(t,c,v){var y,b,O,x=c.children&&c.children[0]&&c.children[0].content&&i.services.interpolator.interpolate(c.children[0].content,d,i.language);if("tag"===c.type){var w=p[parseInt(c.name,10)];!w&&1===f.length&&f[0][c.name]&&(w=f[0][c.name]),w||(w={});var j=0!==Object.keys(c.attrs).length?(y={props:c.attrs},(O=r({},b=w)).props=Object.assign(y.props,b.props),O):w,E=n.isValidElement(j),S=E&&H(c,!0)&&!c.voidElement,N=l&&"object"===a(j)&&j.dummy&&!E,k="object"===a(e)&&null!==e&&Object.hasOwnProperty.call(e,c.name);if("string"==typeof j){var I=i.services.interpolator.interpolate(j,d,i.language);t.push(I)}else if(H(j)||S){m(j,g(j,c,f),t,v)}else if(N){var P=h(p,c.children,f);t.push(n.cloneElement(j,r(r({},j.props),{},{key:v}),P))}else if(Number.isNaN(parseFloat(c.name))){if(k)m(j,g(j,c,f),t,v,c.voidElement);else if(o.transSupportBasicHtmlNodes&&u.indexOf(c.name)>-1)if(c.voidElement)t.push(n.createElement(c.name,{key:"".concat(c.name,"-").concat(v)}));else{var R=h(p,c.children,f);t.push(n.createElement(c.name,{key:"".concat(c.name,"-").concat(v)},R))}else if(c.voidElement)t.push("<".concat(c.name," />"));else{var C=h(p,c.children,f);t.push("<".concat(c.name,">").concat(C,""))}}else if("object"!==a(j)||E)1===c.children.length&&x?t.push(n.cloneElement(j,r(r({},j.props),{},{key:v}),x)):t.push(n.cloneElement(j,r(r({},j.props),{},{key:v})));else{var T=c.children[0]?x:null;T&&t.push(T)}}else if("text"===c.type){var A=o.transWrapTextNodes,L=s?o.unescape(i.services.interpolator.interpolate(c.content,d,i.language)):i.services.interpolator.interpolate(c.content,d,i.language);A?t.push(n.createElement(A,{key:"".concat(c.name,"-").concat(v)},L)):t.push(L)}return t}),[])}return K(h([{dummy:!0,children:e||[]}],p,V(e||[]))[0])}var M=function(e,t){var r=n.useRef();return n.useEffect((function(){r.current=t?r.current:e}),[e,t]),r.current};function $(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},a=t.i18n,i=n.useContext(w)||{},o=i.i18n,c=i.defaultNS,u=a||o||k();if(u&&!u.reportNamespaces&&(u.reportNamespaces=new S),!u){A("You will need to pass in an i18next instance by using initReactI18next");var l=function(e){return Array.isArray(e)?e[e.length-1]:e},f=[l,{},!1];return f.t=l,f.i18n={},f.ready=!1,f}u.options.react&&void 0!==u.options.react.wait&&A("It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.");var p=r(r(r({},E()),u.options.react),t),d=p.useSuspense,g=p.keyPrefix,m=e||c||u.options&&u.options.defaultNS;m="string"==typeof m?[m]:m||["translation"],u.reportNamespaces.addUsedNamespaces&&u.reportNamespaces.addUsedNamespaces(m);var h=(u.isInitialized||u.initializedStoreOnce)&&m.every((function(e){return B(e,u,p)}));function v(){return u.getFixedT(null,"fallback"===p.nsMode?m:m[0],g)}var y=n.useState(v),b=s(y,2),O=b[0],x=b[1],j=m.join(),N=M(j),I=n.useRef(!0);n.useEffect((function(){var e=p.bindI18n,n=p.bindI18nStore;function t(){I.current&&x(v)}return I.current=!0,h||d||L(u,m,(function(){I.current&&x(v)})),h&&N&&N!==j&&I.current&&x(v),e&&u&&u.on(e,t),n&&u&&u.store.on(n,t),function(){I.current=!1,e&&u&&e.split(" ").forEach((function(e){return u.off(e,t)})),n&&u&&n.split(" ").forEach((function(e){return u.store.off(e,t)}))}}),[u,j]);var P=n.useRef(!0);n.useEffect((function(){I.current&&!P.current&&x(v),P.current=!1}),[u,g]);var R=[O,u,h];if(R.t=O,R.i18n=u,R.ready=h,h)return R;if(!h&&!d)return R;throw new Promise((function(e){L(u,m,(function(){e()}))}))}var q=["forwardedRef"];var Y=["ns","children"];function _(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},a=r.i18n,i=n.useContext(w)||{},o=i.i18n,c=a||o||k();c.options&&c.options.isClone||(e&&!c.initializedStoreOnce&&(c.services.resourceStore.data=e,c.options.ns=Object.values(e).reduce((function(e,n){return Object.keys(n).forEach((function(n){e.indexOf(n)<0&&e.push(n)})),e}),c.options.ns),c.initializedStoreOnce=!0,c.isInitialized=!0),t&&!c.initializedLanguageOnce&&(c.changeLanguage(t),c.initializedLanguageOnce=!0))}var J=["initialI18nStore","initialLanguage"];e.I18nContext=w,e.I18nextProvider=function(e){var t=e.i18n,r=e.defaultNS,a=e.children,i=n.useMemo((function(){return{i18n:t,defaultNS:r}}),[t,r]);return n.createElement(w.Provider,{value:i},a)},e.Trans=function(e){var t=e.children,i=e.count,o=e.parent,s=e.i18nKey,u=e.context,l=e.tOptions,f=void 0===l?{}:l,p=e.values,d=e.defaults,g=e.components,m=e.ns,h=e.i18n,v=e.t,y=e.shouldUnescape,b=c(e,F),O=n.useContext(w)||{},x=O.i18n,j=O.defaultNS,S=h||x||k();if(!S)return A("You will need to pass in an i18next instance by using i18nextReactModule"),t;var N=v||S.t.bind(S)||function(e){return e};u&&(f.context=u);var I=r(r({},E()),S.options&&S.options.react),P=m||N.ns||j||S.options&&S.options.defaultNS;P="string"==typeof P?[P]:P||["translation"];var R=d||function e(t,r){if(!t)return"";var i="",o=V(t),s=r.transSupportBasicHtmlNodes&&r.transKeepBasicHtmlNodesFor?r.transKeepBasicHtmlNodesFor:[];return o.forEach((function(t,o){if("string"==typeof t)i+="".concat(t);else if(n.isValidElement(t)){var u=Object.keys(t.props).length,l=s.indexOf(t.type)>-1,f=t.props.children;if(!f&&l&&0===u)i+="<".concat(t.type,"/>");else if(f||l&&0===u)if(t.props.i18nIsDynamicList)i+="<".concat(o,">");else if(l&&1===u&&"string"==typeof f)i+="<".concat(t.type,">").concat(f,"");else{var p=e(f,r);i+="<".concat(o,">").concat(p,"")}else i+="<".concat(o,">")}else if(null===t)C("Trans: the passed in value is invalid - seems you passed in a null child.");else if("object"===a(t)){var d=t.format,g=c(t,D),m=Object.keys(g);if(1===m.length){var h=d?"".concat(m[0],", ").concat(d):m[0];i+="{{".concat(h,"}}")}else C("react-i18next: the passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.",t)}else C("Trans: the passed in value is invalid - seems you passed in a variable like {number} - please pass in variables for interpolation as full objects like {{number}}.",t)})),i}(t,I)||I.transEmptyNodeValue||s,T=I.hashTransKey,L=s||(T?T(R):R),z=p?f.interpolation:{interpolation:r(r({},f.interpolation),{},{prefix:"#$?",suffix:"?$#"})},B=r(r(r(r({},f),{},{count:i},p),z),{},{defaultValue:R,ns:P}),U=W(g||t,L?N(L,B):R,S,I,B,y),H=void 0!==o?o:I.defaultTransParent;return H?n.createElement(H,b,U):U},e.Translation=function(e){var n=e.ns,t=e.children,r=s($(n,c(e,Y)),3),a=r[0],i=r[1],o=r[2];return t(a,{i18n:i,lng:i.language},o)},e.composeInitialProps=P,e.date=function(){return""},e.getDefaults=E,e.getI18n=k,e.getInitialProps=R,e.initReactI18next=I,e.number=function(){return""},e.plural=function(){return""},e.select=function(){return""},e.selectOrdinal=function(){return""},e.setDefaults=j,e.setI18n=N,e.time=function(){return""},e.useSSR=_,e.useTranslation=$,e.withSSR=function(){return function(e){function t(t){var a=t.initialI18nStore,i=t.initialLanguage,o=c(t,J);return _(a,i),n.createElement(e,r({},o))}return t.getInitialProps=P(e),t.displayName="withI18nextSSR(".concat(U(e),")"),t.WrappedComponent=e,t}},e.withTranslation=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return function(a){function i(i){var o=i.forwardedRef,u=c(i,q),l=s($(e,r(r({},u),{},{keyPrefix:t.keyPrefix})),3),f=l[0],p=l[1],d=l[2],g=r(r({},u),{},{t:f,i18n:p,tReady:d});return t.withRef&&o?g.ref=o:!t.withRef&&o&&(g.forwardedRef=o),n.createElement(a,g)}i.displayName="withI18nextTranslation(".concat(U(a),")"),i.WrappedComponent=a;return t.withRef?n.forwardRef((function(e,t){return n.createElement(i,Object.assign({},e,{forwardedRef:t}))})):i}},Object.defineProperty(e,"__esModule",{value:!0})})); +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],n):n((e=e||self).ReactI18next={},e.React)}(this,(function(e,n){"use strict";function t(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function r(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}function c(e,n){return function(e){if(Array.isArray(e))return e}(e)||function(e,n){var t=e&&("undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"]);if(null==t)return;var r,i,a=[],o=!0,s=!1;try{for(t=t.call(e);!(o=(r=t.next()).done)&&(a.push(r.value),!n||a.length!==n);o=!0);}catch(e){s=!0,i=e}finally{try{o||null==t.return||t.return()}finally{if(s)throw i}}return a}(e,n)||function(e,n){if(!e)return;if("string"==typeof e)return u(e,n);var t=Object.prototype.toString.call(e).slice(8,-1);"Object"===t&&e.constructor&&(t=e.constructor.name);if("Map"===t||"Set"===t)return Array.from(e);if("Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t))return u(e,n)}(e,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(e,n){(null==n||n>e.length)&&(n=e.length);for(var t=0,r=new Array(n);t<]+?)[\s/>]|([^\s=]+)=\s?(".*?"|'.*?')/g;function p(e){var n={type:"tag",name:"",voidElement:!1,attrs:{},children:[]},t=e.match(/<\/?([^\s]+?)[/\s>]/);if(t&&(n.name=t[1],(l[t[1]]||"/"===e.charAt(e.length-2))&&(n.voidElement=!0),n.name.startsWith("!--"))){var r=e.indexOf("--\x3e");return{type:"comment",comment:-1!==r?e.slice(4,r):""}}for(var i=new RegExp(f),a=null;null!==(a=i.exec(e));)if(a[0].trim())if(a[1]){var o=a[1].trim(),s=[o,""];o.indexOf("=")>-1&&(s=o.split("=")),n.attrs[s[0]]=s[1],i.lastIndex--}else a[2]&&(n.attrs[a[2]]=a[3].trim().substring(1,a[3].length-1));return n}var d=/<[a-zA-Z0-9\-\!\/](?:"[^"]*"|'[^']*'|[^'">])*>/g,g=/^\s*$/,m=Object.create(null);var h=function(e,n){n||(n={}),n.components||(n.components=m);var t,r=[],i=[],a=-1,o=!1;if(0!==e.indexOf("<")){var s=e.indexOf("<");r.push({type:"text",content:-1===s?e:e.substring(0,s)})}return e.replace(d,(function(s,c){if(o){if(s!=="")return;o=!1}var u,l="/"!==s.charAt(1),f=s.startsWith("\x3c!--"),d=c+s.length,m=e.charAt(d);if(f){var h=p(s);return a<0?(r.push(h),r):((u=i[a]).children.push(h),r)}if(l&&(a++,"tag"===(t=p(s)).type&&n.components[t.name]&&(t.type="component",o=!0),t.voidElement||o||!m||"<"===m||t.children.push({type:"text",content:e.slice(d,e.indexOf("<",d))}),0===a&&r.push(t),(u=i[a-1])&&u.children.push(t),i[a]=t),(!l||t.voidElement)&&(a>-1&&(t.voidElement||t.name===s.slice(2,-1))&&(a--,t=-1===a?r:i[a]),!o&&"<"!==m&&m)){u=-1===a?r:i[a].children;var v=e.indexOf("<",d),y=e.slice(d,-1===v?void 0:v);g.test(y)&&(y=" "),(v>-1&&a+u.length>=0||" "!==y)&&u.push({type:"text",content:y})}})),r};function v(){if(console&&console.warn){for(var e,n=arguments.length,t=new Array(n),r=0;r2&&void 0!==arguments[2]?arguments[2]:{},r=n.languages[0],i=!!n.options&&n.options.fallbackLng,a=n.languages[n.languages.length-1];if("cimode"===r.toLowerCase())return!0;var o=function(e,t){var r=n.services.backendConnector.state["".concat(e,"|").concat(t)];return-1===r||2===r};return!(t.bindI18n&&t.bindI18n.indexOf("languageChanging")>-1&&n.services.backendConnector.backend&&n.isLanguageChangingTo&&!o(n.isLanguageChangingTo,e))&&(!!n.hasResourceBundle(r,e)||(!(n.services.backendConnector.backend&&(!n.options.resources||n.options.partialBundledLanguages))||!(!o(r,e)||i&&!o(a,e))))}function w(e,n){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(!n.languages||!n.languages.length)return b("i18n.languages were undefined or empty",n.languages),!0;var r=void 0!==n.options.ignoreJSONStructure;return r?n.hasLoadedNamespace(e,{precheck:function(n,r){if(t.bindI18n&&t.bindI18n.indexOf("languageChanging")>-1&&n.services.backendConnector.backend&&n.isLanguageChangingTo&&!r(n.isLanguageChangingTo,e))return!1}}):x(e,n,t)}function S(e){return e.displayName||e.name||("string"==typeof e&&e.length>0?e:"Unknown")}var j,E=/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g,N={"&":"&","&":"&","<":"<","<":"<",">":">",">":">","'":"'","'":"'",""":'"',""":'"'," ":" "," ":" ","©":"©","©":"©","®":"®","®":"®","…":"…","…":"…","/":"/","/":"/"},k=function(e){return N[e]},I={bindI18n:"languageChanged",bindI18nStore:"",transEmptyNodeValue:"",transSupportBasicHtmlNodes:!0,transWrapTextNodes:"",transKeepBasicHtmlNodesFor:["br","strong","i","p"],useSuspense:!0,unescape:function(e){return e.replace(E,k)}};function P(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};I=r(r({},I),e)}function R(){return I}function C(e){j=e}function T(){return j}var A=["format"],L=["children","count","parent","i18nKey","context","tOptions","values","defaults","components","ns","i18n","t","shouldUnescape"];function U(e,n){if(!e)return!1;var t=e.props?e.props.children:e.children;return n?t.length>0:!!t}function z(e){return e?e.props?e.props.children:e.children:[]}function B(e){return Array.isArray(e)?e:[e]}function K(e,t,a,o,s,c){if(""===t)return[];var u=o.transKeepBasicHtmlNodesFor||[],l=t&&new RegExp(u.join("|")).test(t);if(!e&&!l)return[t];var f={};!function e(t){B(t).forEach((function(t){"string"!=typeof t&&(U(t)?e(z(t)):"object"!==i(t)||n.isValidElement(t)||Object.assign(f,t))}))}(e);var p=h("<0>".concat(t,"")),d=r(r({},f),s);function g(e,t,r){var i=z(e),a=v(i,t.children,r);return function(e){return"[object Array]"===Object.prototype.toString.call(e)&&e.every((function(e){return n.isValidElement(e)}))}(i)&&0===a.length?i:a}function m(e,t,i,a,o){e.dummy&&(e.children=t),i.push(n.cloneElement(e,r(r({},e.props),{},{key:a}),o?void 0:t))}function v(t,s,f){var p=B(t);return B(s).reduce((function(t,s,h){var y,b,O,x=s.children&&s.children[0]&&s.children[0].content&&a.services.interpolator.interpolate(s.children[0].content,d,a.language);if("tag"===s.type){var w=p[parseInt(s.name,10)];!w&&1===f.length&&f[0][s.name]&&(w=f[0][s.name]),w||(w={});var S=0!==Object.keys(s.attrs).length?(y={props:s.attrs},(O=r({},b=w)).props=Object.assign(y.props,b.props),O):w,j=n.isValidElement(S),E=j&&U(s,!0)&&!s.voidElement,N=l&&"object"===i(S)&&S.dummy&&!j,k="object"===i(e)&&null!==e&&Object.hasOwnProperty.call(e,s.name);if("string"==typeof S){var I=a.services.interpolator.interpolate(S,d,a.language);t.push(I)}else if(U(S)||E){m(S,g(S,s,f),t,h)}else if(N){var P=v(p,s.children,f);t.push(n.cloneElement(S,r(r({},S.props),{},{key:h}),P))}else if(Number.isNaN(parseFloat(s.name))){if(k)m(S,g(S,s,f),t,h,s.voidElement);else if(o.transSupportBasicHtmlNodes&&u.indexOf(s.name)>-1)if(s.voidElement)t.push(n.createElement(s.name,{key:"".concat(s.name,"-").concat(h)}));else{var R=v(p,s.children,f);t.push(n.createElement(s.name,{key:"".concat(s.name,"-").concat(h)},R))}else if(s.voidElement)t.push("<".concat(s.name," />"));else{var C=v(p,s.children,f);t.push("<".concat(s.name,">").concat(C,""))}}else if("object"!==i(S)||j)1===s.children.length&&x?t.push(n.cloneElement(S,r(r({},S.props),{},{key:h}),x)):t.push(n.cloneElement(S,r(r({},S.props),{},{key:h})));else{var T=s.children[0]?x:null;T&&t.push(T)}}else if("text"===s.type){var A=o.transWrapTextNodes,L=c?o.unescape(a.services.interpolator.interpolate(s.content,d,a.language)):a.services.interpolator.interpolate(s.content,d,a.language);A?t.push(n.createElement(A,{key:"".concat(s.name,"-").concat(h)},L)):t.push(L)}return t}),[])}return z(v([{dummy:!0,children:e||[]}],p,B(e||[]))[0])}function D(e){var t=e.children,a=e.count,o=e.parent,c=e.i18nKey,u=e.context,l=e.tOptions,f=void 0===l?{}:l,p=e.values,d=e.defaults,g=e.components,m=e.ns,h=e.i18n,y=e.t,O=e.shouldUnescape,x=s(e,L),w=h||T();if(!w)return b("You will need to pass in an i18next instance by using i18nextReactModule"),t;var S=y||w.t.bind(w)||function(e){return e};u&&(f.context=u);var j=r(r({},R()),w.options&&w.options.react),E=m||S.ns||w.options&&w.options.defaultNS;E="string"==typeof E?[E]:E||["translation"];var N=d||function e(t,r){if(!t)return"";var a="",o=B(t),c=r.transSupportBasicHtmlNodes&&r.transKeepBasicHtmlNodesFor?r.transKeepBasicHtmlNodesFor:[];return o.forEach((function(t,o){if("string"==typeof t)a+="".concat(t);else if(n.isValidElement(t)){var u=Object.keys(t.props).length,l=c.indexOf(t.type)>-1,f=t.props.children;if(!f&&l&&0===u)a+="<".concat(t.type,"/>");else if(f||l&&0===u)if(t.props.i18nIsDynamicList)a+="<".concat(o,">");else if(l&&1===u&&"string"==typeof f)a+="<".concat(t.type,">").concat(f,"");else{var p=e(f,r);a+="<".concat(o,">").concat(p,"")}else a+="<".concat(o,">")}else if(null===t)v("Trans: the passed in value is invalid - seems you passed in a null child.");else if("object"===i(t)){var d=t.format,g=s(t,A),m=Object.keys(g);if(1===m.length){var h=d?"".concat(m[0],", ").concat(d):m[0];a+="{{".concat(h,"}}")}else v("react-i18next: the passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.",t)}else v("Trans: the passed in value is invalid - seems you passed in a variable like {number} - please pass in variables for interpolation as full objects like {{number}}.",t)})),a}(t,j)||j.transEmptyNodeValue||c,k=j.hashTransKey,I=c||(k?k(N):N),P=p?f.interpolation:{interpolation:r(r({},f.interpolation),{},{prefix:"#$?",suffix:"?$#"})},C=r(r(r(r({},f),{},{count:a},p),P),{},{defaultValue:N,ns:E}),U=K(g||t,I?S(I,C):N,w,j,C,O),z=void 0!==o?o:j.defaultTransParent;return z?n.createElement(z,x,U):U}var F={type:"3rdParty",init:function(e){P(e.options.react),C(e)}},H=n.createContext(),V=function(){function e(){!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,e),this.usedNamespaces={}}var n,t,r;return n=e,(t=[{key:"addUsedNamespaces",value:function(e){var n=this;e.forEach((function(e){n.usedNamespaces[e]||(n.usedNamespaces[e]=!0)}))}},{key:"getUsedNamespaces",value:function(){return Object.keys(this.usedNamespaces)}}])&&a(n.prototype,t),r&&a(n,r),e}();function W(e){return function(n){return new Promise((function(t){var i=M();e.getInitialProps?e.getInitialProps(n).then((function(e){t(r(r({},e),i))})):t(i)}))}}function M(){var e=T(),n=e.reportNamespaces?e.reportNamespaces.getUsedNamespaces():[],t={},r={};return e.languages.forEach((function(t){r[t]={},n.forEach((function(n){r[t][n]=e.getResourceBundle(t,n)||{}}))})),t.initialI18nStore=r,t.initialLanguage=e.language,t}var $=["children","count","parent","i18nKey","context","tOptions","values","defaults","components","ns","i18n","t","shouldUnescape"];var q=function(e,t){var r=n.useRef();return n.useEffect((function(){r.current=t?r.current:e}),[e,t]),r.current};function Y(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=t.i18n,a=n.useContext(H)||{},o=a.i18n,s=a.defaultNS,u=i||o||T();if(u&&!u.reportNamespaces&&(u.reportNamespaces=new V),!u){b("You will need to pass in an i18next instance by using initReactI18next");var l=function(e){return Array.isArray(e)?e[e.length-1]:e},f=[l,{},!1];return f.t=l,f.i18n={},f.ready=!1,f}u.options.react&&void 0!==u.options.react.wait&&b("It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.");var p=r(r(r({},R()),u.options.react),t),d=p.useSuspense,g=p.keyPrefix,m=e||s||u.options&&u.options.defaultNS;m="string"==typeof m?[m]:m||["translation"],u.reportNamespaces.addUsedNamespaces&&u.reportNamespaces.addUsedNamespaces(m);var h=(u.isInitialized||u.initializedStoreOnce)&&m.every((function(e){return w(e,u,p)}));function v(){return u.getFixedT(null,"fallback"===p.nsMode?m:m[0],g)}var y=n.useState(v),x=c(y,2),S=x[0],j=x[1],E=m.join(),N=q(E),k=n.useRef(!0);n.useEffect((function(){var e=p.bindI18n,n=p.bindI18nStore;function t(){k.current&&j(v)}return k.current=!0,h||d||O(u,m,(function(){k.current&&j(v)})),h&&N&&N!==E&&k.current&&j(v),e&&u&&u.on(e,t),n&&u&&u.store.on(n,t),function(){k.current=!1,e&&u&&e.split(" ").forEach((function(e){return u.off(e,t)})),n&&u&&n.split(" ").forEach((function(e){return u.store.off(e,t)}))}}),[u,E]);var I=n.useRef(!0);n.useEffect((function(){k.current&&!I.current&&j(v),I.current=!1}),[u,g]);var P=[S,u,h];if(P.t=S,P.i18n=u,P.ready=h,h)return P;if(!h&&!d)return P;throw new Promise((function(e){O(u,m,(function(){e()}))}))}var _=["forwardedRef"];var J=["ns","children"];function Z(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=r.i18n,a=n.useContext(H)||{},o=a.i18n,s=i||o||T();s.options&&s.options.isClone||(e&&!s.initializedStoreOnce&&(s.services.resourceStore.data=e,s.options.ns=Object.values(e).reduce((function(e,n){return Object.keys(n).forEach((function(n){e.indexOf(n)<0&&e.push(n)})),e}),s.options.ns),s.initializedStoreOnce=!0,s.isInitialized=!0),t&&!s.initializedLanguageOnce&&(s.changeLanguage(t),s.initializedLanguageOnce=!0))}var G=["initialI18nStore","initialLanguage"];e.I18nContext=H,e.I18nextProvider=function(e){var t=e.i18n,r=e.defaultNS,i=e.children,a=n.useMemo((function(){return{i18n:t,defaultNS:r}}),[t,r]);return n.createElement(H.Provider,{value:a},i)},e.Trans=function(e){var t=e.children,i=e.count,a=e.parent,o=e.i18nKey,c=e.context,u=e.tOptions,l=void 0===u?{}:u,f=e.values,p=e.defaults,d=e.components,g=e.ns,m=e.i18n,h=e.t,v=e.shouldUnescape,y=s(e,$),b=n.useContext(H)||{},O=b.i18n,x=b.defaultNS,w=m||O||T(),S=h||w.t.bind(w)||function(e){return e};return D(r({children:t,count:i,parent:a,i18nKey:o,context:c,tOptions:l,values:f,defaults:p,components:d,ns:g||S.ns||x||w.options&&w.options.defaultNS,i18n:w,t:h,shouldUnescape:v},y))},e.TransWithoutContext=D,e.Translation=function(e){var n=e.ns,t=e.children,r=c(Y(n,s(e,J)),3),i=r[0],a=r[1],o=r[2];return t(i,{i18n:a,lng:a.language},o)},e.composeInitialProps=W,e.date=function(){return""},e.getDefaults=R,e.getI18n=T,e.getInitialProps=M,e.initReactI18next=F,e.number=function(){return""},e.plural=function(){return""},e.select=function(){return""},e.selectOrdinal=function(){return""},e.setDefaults=P,e.setI18n=C,e.time=function(){return""},e.useSSR=Z,e.useTranslation=Y,e.withSSR=function(){return function(e){function t(t){var i=t.initialI18nStore,a=t.initialLanguage,o=s(t,G);return Z(i,a),n.createElement(e,r({},o))}return t.getInitialProps=W(e),t.displayName="withI18nextSSR(".concat(S(e),")"),t.WrappedComponent=e,t}},e.withTranslation=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return function(i){function a(a){var o=a.forwardedRef,u=s(a,_),l=c(Y(e,r(r({},u),{},{keyPrefix:t.keyPrefix})),3),f=l[0],p=l[1],d=l[2],g=r(r({},u),{},{t:f,i18n:p,tReady:d});return t.withRef&&o?g.ref=o:!t.withRef&&o&&(g.forwardedRef=o),n.createElement(i,g)}a.displayName="withI18nextTranslation(".concat(S(i),")"),a.WrappedComponent=i;return t.withRef?n.forwardRef((function(e,t){return n.createElement(a,Object.assign({},e,{forwardedRef:t}))})):a}},Object.defineProperty(e,"__esModule",{value:!0})})); diff --git a/src/Trans.js b/src/Trans.js index 33307291..d021ebba 100644 --- a/src/Trans.js +++ b/src/Trans.js @@ -1,276 +1,8 @@ -import { useContext, isValidElement, cloneElement, createElement } from 'react'; -import HTML from 'html-parse-stringify'; -import { getI18n, I18nContext, getDefaults } from './context'; -import { warn, warnOnce } from './utils'; +import { useContext } from 'react'; +import { nodesToString, Trans as TransWithoutContext } from './TransWithoutContext'; +import { getI18n, I18nContext } from './context'; -function hasChildren(node, checkLength) { - if (!node) return false; - const base = node.props ? node.props.children : node.children; - if (checkLength) return base.length > 0; - return !!base; -} - -function getChildren(node) { - if (!node) return []; - return node.props ? node.props.children : node.children; -} - -function hasValidReactChildren(children) { - if (Object.prototype.toString.call(children) !== '[object Array]') return false; - return children.every((child) => isValidElement(child)); -} - -function getAsArray(data) { - return Array.isArray(data) ? data : [data]; -} - -function mergeProps(source, target) { - const newTarget = { ...target }; - // overwrite source.props when target.props already set - newTarget.props = Object.assign(source.props, target.props); - return newTarget; -} - -export function nodesToString(children, i18nOptions) { - if (!children) return ''; - let stringNode = ''; - - // do not use `React.Children.toArray`, will fail at object children - const childrenArray = getAsArray(children); - const keepArray = - i18nOptions.transSupportBasicHtmlNodes && i18nOptions.transKeepBasicHtmlNodesFor - ? i18nOptions.transKeepBasicHtmlNodesFor - : []; - - // e.g. lorem
ipsum {{ messageCount, format }} dolor bold amet - childrenArray.forEach((child, childIndex) => { - if (typeof child === 'string') { - // actual e.g. lorem - // expected e.g. lorem - stringNode += `${child}`; - } else if (isValidElement(child)) { - const childPropsCount = Object.keys(child.props).length; - const shouldKeepChild = keepArray.indexOf(child.type) > -1; - const childChildren = child.props.children; - - if (!childChildren && shouldKeepChild && childPropsCount === 0) { - // actual e.g. lorem
ipsum - // expected e.g. lorem
ipsum - stringNode += `<${child.type}/>`; - } else if (!childChildren && (!shouldKeepChild || childPropsCount !== 0)) { - // actual e.g. lorem
ipsum - // expected e.g. lorem <0> ipsum - stringNode += `<${childIndex}>`; - } else if (child.props.i18nIsDynamicList) { - // we got a dynamic list like - // e.g.
    {['a', 'b'].map(item => (
  • {item}
  • ))}
- // expected e.g. "<0>", not e.g. "<0><0>a<1>b" - stringNode += `<${childIndex}>`; - } else if (shouldKeepChild && childPropsCount === 1 && typeof childChildren === 'string') { - // actual e.g. dolor bold amet - // expected e.g. dolor bold amet - stringNode += `<${child.type}>${childChildren}`; - } else { - // regular case mapping the inner children - const content = nodesToString(childChildren, i18nOptions); - stringNode += `<${childIndex}>${content}`; - } - } else if (child === null) { - warn(`Trans: the passed in value is invalid - seems you passed in a null child.`); - } else if (typeof child === 'object') { - // e.g. lorem {{ value, format }} ipsum - const { format, ...clone } = child; - const keys = Object.keys(clone); - - if (keys.length === 1) { - const value = format ? `${keys[0]}, ${format}` : keys[0]; - stringNode += `{{${value}}}`; - } else { - // not a valid interpolation object (can only contain one value plus format) - warn( - `react-i18next: the passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.`, - child, - ); - } - } else { - warn( - `Trans: the passed in value is invalid - seems you passed in a variable like {number} - please pass in variables for interpolation as full objects like {{number}}.`, - child, - ); - } - }); - - return stringNode; -} - -function renderNodes(children, targetString, i18n, i18nOptions, combinedTOpts, shouldUnescape) { - if (targetString === '') return []; - - // check if contains tags we need to replace from html string to react nodes - const keepArray = i18nOptions.transKeepBasicHtmlNodesFor || []; - const emptyChildrenButNeedsHandling = - targetString && new RegExp(keepArray.join('|')).test(targetString); - - // no need to replace tags in the targetstring - if (!children && !emptyChildrenButNeedsHandling) return [targetString]; - - // v2 -> interpolates upfront no need for "some <0>{{var}}"" -> will be just "some {{var}}" in translation file - const data = {}; - - function getData(childs) { - const childrenArray = getAsArray(childs); - - childrenArray.forEach((child) => { - if (typeof child === 'string') return; - if (hasChildren(child)) getData(getChildren(child)); - else if (typeof child === 'object' && !isValidElement(child)) Object.assign(data, child); - }); - } - - getData(children); - - // parse ast from string with additional wrapper tag - // -> avoids issues in parser removing prepending text nodes - const ast = HTML.parse(`<0>${targetString}`); - const opts = { ...data, ...combinedTOpts }; - - function renderInner(child, node, rootReactNode) { - const childs = getChildren(child); - const mappedChildren = mapAST(childs, node.children, rootReactNode); - // console.warn('INNER', node.name, node, child, childs, node.children, mappedChildren); - return hasValidReactChildren(childs) && mappedChildren.length === 0 ? childs : mappedChildren; - } - - function pushTranslatedJSX(child, inner, mem, i, isVoid) { - if (child.dummy) child.children = inner; // needed on preact! - mem.push(cloneElement(child, { ...child.props, key: i }, isVoid ? undefined : inner)); - } - - // reactNode (the jsx root element or child) - // astNode (the translation string as html ast) - // rootReactNode (the most outer jsx children array or trans components prop) - function mapAST(reactNode, astNode, rootReactNode) { - const reactNodes = getAsArray(reactNode); - const astNodes = getAsArray(astNode); - - return astNodes.reduce((mem, node, i) => { - const translationContent = - node.children && - node.children[0] && - node.children[0].content && - i18n.services.interpolator.interpolate(node.children[0].content, opts, i18n.language); - - if (node.type === 'tag') { - let tmp = reactNodes[parseInt(node.name, 10)]; // regular array (components or children) - if (!tmp && rootReactNode.length === 1 && rootReactNode[0][node.name]) - tmp = rootReactNode[0][node.name]; // trans components is an object - if (!tmp) tmp = {}; - // console.warn('TMP', node.name, parseInt(node.name, 10), tmp, reactNodes); - const child = - Object.keys(node.attrs).length !== 0 ? mergeProps({ props: node.attrs }, tmp) : tmp; - - const isElement = isValidElement(child); - - const isValidTranslationWithChildren = - isElement && hasChildren(node, true) && !node.voidElement; - - const isEmptyTransWithHTML = - emptyChildrenButNeedsHandling && typeof child === 'object' && child.dummy && !isElement; - - const isKnownComponent = - typeof children === 'object' && - children !== null && - Object.hasOwnProperty.call(children, node.name); - // console.warn('CHILD', node.name, node, isElement, child); - - if (typeof child === 'string') { - const value = i18n.services.interpolator.interpolate(child, opts, i18n.language); - mem.push(value); - } else if ( - hasChildren(child) || // the jsx element has children -> loop - isValidTranslationWithChildren // valid jsx element with no children but the translation has -> loop - ) { - const inner = renderInner(child, node, rootReactNode); - pushTranslatedJSX(child, inner, mem, i); - } else if (isEmptyTransWithHTML) { - // we have a empty Trans node (the dummy element) with a targetstring that contains html tags needing - // conversion to react nodes - // so we just need to map the inner stuff - const inner = mapAST( - reactNodes /* wrong but we need something */, - node.children, - rootReactNode, - ); - mem.push(cloneElement(child, { ...child.props, key: i }, inner)); - } else if (Number.isNaN(parseFloat(node.name))) { - if (isKnownComponent) { - const inner = renderInner(child, node, rootReactNode); - pushTranslatedJSX(child, inner, mem, i, node.voidElement); - } else if (i18nOptions.transSupportBasicHtmlNodes && keepArray.indexOf(node.name) > -1) { - if (node.voidElement) { - mem.push(createElement(node.name, { key: `${node.name}-${i}` })); - } else { - const inner = mapAST( - reactNodes /* wrong but we need something */, - node.children, - rootReactNode, - ); - - mem.push(createElement(node.name, { key: `${node.name}-${i}` }, inner)); - } - } else if (node.voidElement) { - mem.push(`<${node.name} />`); - } else { - const inner = mapAST( - reactNodes /* wrong but we need something */, - node.children, - rootReactNode, - ); - - mem.push(`<${node.name}>${inner}`); - } - } else if (typeof child === 'object' && !isElement) { - const content = node.children[0] ? translationContent : null; - - // v1 - // as interpolation was done already we just have a regular content node - // in the translation AST while having an object in reactNodes - // -> push the content no need to interpolate again - if (content) mem.push(content); - } else if (node.children.length === 1 && translationContent) { - // If component does not have children, but translation - has - // with this in component could be components={[]} and in translation - 'some text <0>some highlighted message' - mem.push(cloneElement(child, { ...child.props, key: i }, translationContent)); - } else { - mem.push(cloneElement(child, { ...child.props, key: i })); - } - } else if (node.type === 'text') { - const wrapTextNodes = i18nOptions.transWrapTextNodes; - const content = shouldUnescape - ? i18nOptions.unescape( - i18n.services.interpolator.interpolate(node.content, opts, i18n.language), - ) - : i18n.services.interpolator.interpolate(node.content, opts, i18n.language); - if (wrapTextNodes) { - mem.push(createElement(wrapTextNodes, { key: `${node.name}-${i}` }, content)); - } else { - mem.push(content); - } - } - return mem; - }, []); - } - - // call mapAST with having react nodes nested into additional node like - // we did for the string ast from translation - // return the children of that extra node to get expected result - const result = mapAST( - [{ dummy: true, children: children || [] }], - ast, - getAsArray(children || []), - ); - return getChildren(result[0]); -} +export { nodesToString }; export function Trans({ children, @@ -291,53 +23,23 @@ export function Trans({ const { i18n: i18nFromContext, defaultNS: defaultNSFromContext } = useContext(I18nContext) || {}; const i18n = i18nFromProps || i18nFromContext || getI18n(); - if (!i18n) { - warnOnce('You will need to pass in an i18next instance by using i18nextReactModule'); - return children; - } - const t = tFromProps || i18n.t.bind(i18n) || ((k) => k); - if (context) tOptions.context = context; - - const reactI18nextOptions = { ...getDefaults(), ...(i18n.options && i18n.options.react) }; - - // prepare having a namespace - let namespaces = ns || t.ns || defaultNSFromContext || (i18n.options && i18n.options.defaultNS); - namespaces = typeof namespaces === 'string' ? [namespaces] : namespaces || ['translation']; - - const defaultValue = - defaults || - nodesToString(children, reactI18nextOptions) || - reactI18nextOptions.transEmptyNodeValue || - i18nKey; - const { hashTransKey } = reactI18nextOptions; - const key = i18nKey || (hashTransKey ? hashTransKey(defaultValue) : defaultValue); - const interpolationOverride = values - ? tOptions.interpolation - : { interpolation: { ...tOptions.interpolation, prefix: '#$?', suffix: '?$#' } }; - const combinedTOpts = { - ...tOptions, + return TransWithoutContext({ + children, count, - ...values, - ...interpolationOverride, - defaultValue, - ns: namespaces, - }; - const translation = key ? t(key, combinedTOpts) : defaultValue; - - const content = renderNodes( - components || children, - translation, + parent, + i18nKey, + context, + tOptions, + values, + defaults, + components, + // prepare having a namespace + ns: ns || t.ns || defaultNSFromContext || (i18n.options && i18n.options.defaultNS), i18n, - reactI18nextOptions, - combinedTOpts, + t: tFromProps, shouldUnescape, - ); - - // allows user to pass `null` to `parent` - // and override `defaultTransParent` if is present - const useAsParent = parent !== undefined ? parent : reactI18nextOptions.defaultTransParent; - - return useAsParent ? createElement(useAsParent, additionalProps, content) : content; + ...additionalProps, + }); } diff --git a/src/TransWithoutContext.js b/src/TransWithoutContext.js new file mode 100644 index 00000000..8633b185 --- /dev/null +++ b/src/TransWithoutContext.js @@ -0,0 +1,343 @@ +import { isValidElement, cloneElement, createElement } from 'react'; +import HTML from 'html-parse-stringify'; +import { warn, warnOnce } from './utils'; +import { getDefaults } from './defaults'; +import { getI18n } from './i18nInstance'; + +function hasChildren(node, checkLength) { + if (!node) return false; + const base = node.props ? node.props.children : node.children; + if (checkLength) return base.length > 0; + return !!base; +} + +function getChildren(node) { + if (!node) return []; + return node.props ? node.props.children : node.children; +} + +function hasValidReactChildren(children) { + if (Object.prototype.toString.call(children) !== '[object Array]') return false; + return children.every((child) => isValidElement(child)); +} + +function getAsArray(data) { + return Array.isArray(data) ? data : [data]; +} + +function mergeProps(source, target) { + const newTarget = { ...target }; + // overwrite source.props when target.props already set + newTarget.props = Object.assign(source.props, target.props); + return newTarget; +} + +export function nodesToString(children, i18nOptions) { + if (!children) return ''; + let stringNode = ''; + + // do not use `React.Children.toArray`, will fail at object children + const childrenArray = getAsArray(children); + const keepArray = + i18nOptions.transSupportBasicHtmlNodes && i18nOptions.transKeepBasicHtmlNodesFor + ? i18nOptions.transKeepBasicHtmlNodesFor + : []; + + // e.g. lorem
ipsum {{ messageCount, format }} dolor bold amet + childrenArray.forEach((child, childIndex) => { + if (typeof child === 'string') { + // actual e.g. lorem + // expected e.g. lorem + stringNode += `${child}`; + } else if (isValidElement(child)) { + const childPropsCount = Object.keys(child.props).length; + const shouldKeepChild = keepArray.indexOf(child.type) > -1; + const childChildren = child.props.children; + + if (!childChildren && shouldKeepChild && childPropsCount === 0) { + // actual e.g. lorem
ipsum + // expected e.g. lorem
ipsum + stringNode += `<${child.type}/>`; + } else if (!childChildren && (!shouldKeepChild || childPropsCount !== 0)) { + // actual e.g. lorem
ipsum + // expected e.g. lorem <0> ipsum + stringNode += `<${childIndex}>`; + } else if (child.props.i18nIsDynamicList) { + // we got a dynamic list like + // e.g.
    {['a', 'b'].map(item => (
  • {item}
  • ))}
+ // expected e.g. "<0>", not e.g. "<0><0>a<1>b" + stringNode += `<${childIndex}>`; + } else if (shouldKeepChild && childPropsCount === 1 && typeof childChildren === 'string') { + // actual e.g. dolor bold amet + // expected e.g. dolor bold amet + stringNode += `<${child.type}>${childChildren}`; + } else { + // regular case mapping the inner children + const content = nodesToString(childChildren, i18nOptions); + stringNode += `<${childIndex}>${content}`; + } + } else if (child === null) { + warn(`Trans: the passed in value is invalid - seems you passed in a null child.`); + } else if (typeof child === 'object') { + // e.g. lorem {{ value, format }} ipsum + const { format, ...clone } = child; + const keys = Object.keys(clone); + + if (keys.length === 1) { + const value = format ? `${keys[0]}, ${format}` : keys[0]; + stringNode += `{{${value}}}`; + } else { + // not a valid interpolation object (can only contain one value plus format) + warn( + `react-i18next: the passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.`, + child, + ); + } + } else { + warn( + `Trans: the passed in value is invalid - seems you passed in a variable like {number} - please pass in variables for interpolation as full objects like {{number}}.`, + child, + ); + } + }); + + return stringNode; +} + +function renderNodes(children, targetString, i18n, i18nOptions, combinedTOpts, shouldUnescape) { + if (targetString === '') return []; + + // check if contains tags we need to replace from html string to react nodes + const keepArray = i18nOptions.transKeepBasicHtmlNodesFor || []; + const emptyChildrenButNeedsHandling = + targetString && new RegExp(keepArray.join('|')).test(targetString); + + // no need to replace tags in the targetstring + if (!children && !emptyChildrenButNeedsHandling) return [targetString]; + + // v2 -> interpolates upfront no need for "some <0>{{var}}"" -> will be just "some {{var}}" in translation file + const data = {}; + + function getData(childs) { + const childrenArray = getAsArray(childs); + + childrenArray.forEach((child) => { + if (typeof child === 'string') return; + if (hasChildren(child)) getData(getChildren(child)); + else if (typeof child === 'object' && !isValidElement(child)) Object.assign(data, child); + }); + } + + getData(children); + + // parse ast from string with additional wrapper tag + // -> avoids issues in parser removing prepending text nodes + const ast = HTML.parse(`<0>${targetString}`); + const opts = { ...data, ...combinedTOpts }; + + function renderInner(child, node, rootReactNode) { + const childs = getChildren(child); + const mappedChildren = mapAST(childs, node.children, rootReactNode); + // console.warn('INNER', node.name, node, child, childs, node.children, mappedChildren); + return hasValidReactChildren(childs) && mappedChildren.length === 0 ? childs : mappedChildren; + } + + function pushTranslatedJSX(child, inner, mem, i, isVoid) { + if (child.dummy) child.children = inner; // needed on preact! + mem.push(cloneElement(child, { ...child.props, key: i }, isVoid ? undefined : inner)); + } + + // reactNode (the jsx root element or child) + // astNode (the translation string as html ast) + // rootReactNode (the most outer jsx children array or trans components prop) + function mapAST(reactNode, astNode, rootReactNode) { + const reactNodes = getAsArray(reactNode); + const astNodes = getAsArray(astNode); + + return astNodes.reduce((mem, node, i) => { + const translationContent = + node.children && + node.children[0] && + node.children[0].content && + i18n.services.interpolator.interpolate(node.children[0].content, opts, i18n.language); + + if (node.type === 'tag') { + let tmp = reactNodes[parseInt(node.name, 10)]; // regular array (components or children) + if (!tmp && rootReactNode.length === 1 && rootReactNode[0][node.name]) + tmp = rootReactNode[0][node.name]; // trans components is an object + if (!tmp) tmp = {}; + // console.warn('TMP', node.name, parseInt(node.name, 10), tmp, reactNodes); + const child = + Object.keys(node.attrs).length !== 0 ? mergeProps({ props: node.attrs }, tmp) : tmp; + + const isElement = isValidElement(child); + + const isValidTranslationWithChildren = + isElement && hasChildren(node, true) && !node.voidElement; + + const isEmptyTransWithHTML = + emptyChildrenButNeedsHandling && typeof child === 'object' && child.dummy && !isElement; + + const isKnownComponent = + typeof children === 'object' && + children !== null && + Object.hasOwnProperty.call(children, node.name); + // console.warn('CHILD', node.name, node, isElement, child); + + if (typeof child === 'string') { + const value = i18n.services.interpolator.interpolate(child, opts, i18n.language); + mem.push(value); + } else if ( + hasChildren(child) || // the jsx element has children -> loop + isValidTranslationWithChildren // valid jsx element with no children but the translation has -> loop + ) { + const inner = renderInner(child, node, rootReactNode); + pushTranslatedJSX(child, inner, mem, i); + } else if (isEmptyTransWithHTML) { + // we have a empty Trans node (the dummy element) with a targetstring that contains html tags needing + // conversion to react nodes + // so we just need to map the inner stuff + const inner = mapAST( + reactNodes /* wrong but we need something */, + node.children, + rootReactNode, + ); + mem.push(cloneElement(child, { ...child.props, key: i }, inner)); + } else if (Number.isNaN(parseFloat(node.name))) { + if (isKnownComponent) { + const inner = renderInner(child, node, rootReactNode); + pushTranslatedJSX(child, inner, mem, i, node.voidElement); + } else if (i18nOptions.transSupportBasicHtmlNodes && keepArray.indexOf(node.name) > -1) { + if (node.voidElement) { + mem.push(createElement(node.name, { key: `${node.name}-${i}` })); + } else { + const inner = mapAST( + reactNodes /* wrong but we need something */, + node.children, + rootReactNode, + ); + + mem.push(createElement(node.name, { key: `${node.name}-${i}` }, inner)); + } + } else if (node.voidElement) { + mem.push(`<${node.name} />`); + } else { + const inner = mapAST( + reactNodes /* wrong but we need something */, + node.children, + rootReactNode, + ); + + mem.push(`<${node.name}>${inner}`); + } + } else if (typeof child === 'object' && !isElement) { + const content = node.children[0] ? translationContent : null; + + // v1 + // as interpolation was done already we just have a regular content node + // in the translation AST while having an object in reactNodes + // -> push the content no need to interpolate again + if (content) mem.push(content); + } else if (node.children.length === 1 && translationContent) { + // If component does not have children, but translation - has + // with this in component could be components={[]} and in translation - 'some text <0>some highlighted message' + mem.push(cloneElement(child, { ...child.props, key: i }, translationContent)); + } else { + mem.push(cloneElement(child, { ...child.props, key: i })); + } + } else if (node.type === 'text') { + const wrapTextNodes = i18nOptions.transWrapTextNodes; + const content = shouldUnescape + ? i18nOptions.unescape( + i18n.services.interpolator.interpolate(node.content, opts, i18n.language), + ) + : i18n.services.interpolator.interpolate(node.content, opts, i18n.language); + if (wrapTextNodes) { + mem.push(createElement(wrapTextNodes, { key: `${node.name}-${i}` }, content)); + } else { + mem.push(content); + } + } + return mem; + }, []); + } + + // call mapAST with having react nodes nested into additional node like + // we did for the string ast from translation + // return the children of that extra node to get expected result + const result = mapAST( + [{ dummy: true, children: children || [] }], + ast, + getAsArray(children || []), + ); + return getChildren(result[0]); +} + +export function Trans({ + children, + count, + parent, + i18nKey, + context, + tOptions = {}, + values, + defaults, + components, + ns, + i18n: i18nFromProps, + t: tFromProps, + shouldUnescape, + ...additionalProps +}) { + const i18n = i18nFromProps || getI18n(); + + if (!i18n) { + warnOnce('You will need to pass in an i18next instance by using i18nextReactModule'); + return children; + } + + const t = tFromProps || i18n.t.bind(i18n) || ((k) => k); + + if (context) tOptions.context = context; + + const reactI18nextOptions = { ...getDefaults(), ...(i18n.options && i18n.options.react) }; + + // prepare having a namespace + let namespaces = ns || t.ns || (i18n.options && i18n.options.defaultNS); + namespaces = typeof namespaces === 'string' ? [namespaces] : namespaces || ['translation']; + + const defaultValue = + defaults || + nodesToString(children, reactI18nextOptions) || + reactI18nextOptions.transEmptyNodeValue || + i18nKey; + const { hashTransKey } = reactI18nextOptions; + const key = i18nKey || (hashTransKey ? hashTransKey(defaultValue) : defaultValue); + const interpolationOverride = values + ? tOptions.interpolation + : { interpolation: { ...tOptions.interpolation, prefix: '#$?', suffix: '?$#' } }; + const combinedTOpts = { + ...tOptions, + count, + ...values, + ...interpolationOverride, + defaultValue, + ns: namespaces, + }; + const translation = key ? t(key, combinedTOpts) : defaultValue; + + const content = renderNodes( + components || children, + translation, + i18n, + reactI18nextOptions, + combinedTOpts, + shouldUnescape, + ); + + // allows user to pass `null` to `parent` + // and override `defaultTransParent` if is present + const useAsParent = parent !== undefined ? parent : reactI18nextOptions.defaultTransParent; + + return useAsParent ? createElement(useAsParent, additionalProps, content) : content; +} diff --git a/src/context.js b/src/context.js index d33664ad..639086a2 100644 --- a/src/context.js +++ b/src/context.js @@ -1,31 +1,12 @@ import { createContext } from 'react'; -import { unescape } from './unescape'; +import { getDefaults, setDefaults } from './defaults'; +import { getI18n, setI18n } from './i18nInstance'; +import { initReactI18next } from './initReactI18next'; -let defaultOptions = { - bindI18n: 'languageChanged', - bindI18nStore: '', - // nsMode: 'fallback' // loop through all namespaces given to hook, HOC, render prop for key lookup - transEmptyNodeValue: '', - transSupportBasicHtmlNodes: true, - transWrapTextNodes: '', - transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'], - // hashTransKey: key => key // calculate a key for Trans component based on defaultValue - useSuspense: true, - unescape, -}; - -let i18nInstance; +export { getDefaults, setDefaults, getI18n, setI18n, initReactI18next }; export const I18nContext = createContext(); -export function setDefaults(options = {}) { - defaultOptions = { ...defaultOptions, ...options }; -} - -export function getDefaults() { - return defaultOptions; -} - export class ReportNamespaces { constructor() { this.usedNamespaces = {}; @@ -42,23 +23,6 @@ export class ReportNamespaces { } } -export function setI18n(instance) { - i18nInstance = instance; -} - -export function getI18n() { - return i18nInstance; -} - -export const initReactI18next = { - type: '3rdParty', - - init(instance) { - setDefaults(instance.options.react); - setI18n(instance); - }, -}; - export function composeInitialProps(ForComponent) { return (ctx) => new Promise((resolve) => { diff --git a/src/defaults.js b/src/defaults.js new file mode 100644 index 00000000..441c1f44 --- /dev/null +++ b/src/defaults.js @@ -0,0 +1,22 @@ +import { unescape } from './unescape'; + +let defaultOptions = { + bindI18n: 'languageChanged', + bindI18nStore: '', + // nsMode: 'fallback' // loop through all namespaces given to hook, HOC, render prop for key lookup + transEmptyNodeValue: '', + transSupportBasicHtmlNodes: true, + transWrapTextNodes: '', + transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'], + // hashTransKey: key => key // calculate a key for Trans component based on defaultValue + useSuspense: true, + unescape, +}; + +export function setDefaults(options = {}) { + defaultOptions = { ...defaultOptions, ...options }; +} + +export function getDefaults() { + return defaultOptions; +} diff --git a/src/i18nInstance.js b/src/i18nInstance.js new file mode 100644 index 00000000..62171f6b --- /dev/null +++ b/src/i18nInstance.js @@ -0,0 +1,9 @@ +let i18nInstance; + +export function setI18n(instance) { + i18nInstance = instance; +} + +export function getI18n() { + return i18nInstance; +} diff --git a/src/index.js b/src/index.js index 248a1ee3..c6f0abc2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ export { Trans } from './Trans'; +export { Trans as TransWithoutContext } from './TransWithoutContext'; export { useTranslation } from './useTranslation'; export { withTranslation } from './withTranslation'; export { Translation } from './Translation'; diff --git a/src/initReactI18next.js b/src/initReactI18next.js new file mode 100644 index 00000000..cdb9a847 --- /dev/null +++ b/src/initReactI18next.js @@ -0,0 +1,11 @@ +import { setDefaults } from './defaults'; +import { setI18n } from './i18nInstance'; + +export const initReactI18next = { + type: '3rdParty', + + init(instance) { + setDefaults(instance.options.react); + setI18n(instance); + }, +}; diff --git a/test/typescript/TransWithoutContext.test.tsx b/test/typescript/TransWithoutContext.test.tsx new file mode 100644 index 00000000..bb759118 --- /dev/null +++ b/test/typescript/TransWithoutContext.test.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Trans } from '../../TransWithoutContext'; + +function basic() { + return Foo; +} + +function children() { + return ( + +
+ + ); +} + +function components() { + return ]}>Foo; +} + +function constComponents() { + const constArray = [
] as const; + return Foo; +} + +function objectComponents() { + return }} defaults="Hello " />; +} + +function MyComponent() { + return <>world; +} + +function objectCustomComponents() { + return }} defaults="Hello " />; +} + +function constObjectComponents() { + const constObject = { + Btn: