選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 

254 行
11 KiB

  1. import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
  2. import _defineProperty from "@babel/runtime/helpers/defineProperty";
  3. import _typeof from "@babel/runtime/helpers/typeof";
  4. function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
  5. function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
  6. import React, { useContext } from 'react';
  7. import HTML from 'html-parse-stringify2';
  8. import { getI18n, getHasUsedI18nextProvider, I18nContext, getDefaults } from './context';
  9. import { warn, warnOnce } from './utils';
  10. function hasChildren(node) {
  11. return node && (node.children || node.props && node.props.children);
  12. }
  13. function getChildren(node) {
  14. if (!node) return [];
  15. return node && node.children ? node.children : node.props && node.props.children;
  16. }
  17. function hasValidReactChildren(children) {
  18. if (Object.prototype.toString.call(children) !== '[object Array]') return false;
  19. return children.every(function (child) {
  20. return React.isValidElement(child);
  21. });
  22. }
  23. function getAsArray(data) {
  24. return Array.isArray(data) ? data : [data];
  25. }
  26. export function nodesToString(startingString, children, index, i18nOptions) {
  27. if (!children) return '';
  28. var stringNode = startingString;
  29. var childrenArray = getAsArray(children);
  30. var keepArray = i18nOptions.transKeepBasicHtmlNodesFor || [];
  31. childrenArray.forEach(function (child, i) {
  32. var elementKey = "".concat(i);
  33. if (typeof child === 'string') {
  34. stringNode = "".concat(stringNode).concat(child);
  35. } else if (hasChildren(child)) {
  36. var elementTag = keepArray.indexOf(child.type) > -1 && Object.keys(child.props).length === 1 && typeof hasChildren(child) === 'string' ? child.type : elementKey;
  37. if (child.props && child.props.i18nIsDynamicList) {
  38. // we got a dynamic list like "<ul>{['a', 'b'].map(item => ( <li key={item}>{item}</li> ))}</ul>""
  39. // the result should be "<0></0>" and not "<0><0>a</0><1>b</1></0>"
  40. stringNode = "".concat(stringNode, "<").concat(elementTag, "></").concat(elementTag, ">");
  41. } else {
  42. // regular case mapping the inner children
  43. stringNode = "".concat(stringNode, "<").concat(elementTag, ">").concat(nodesToString('', getChildren(child), i + 1, i18nOptions), "</").concat(elementTag, ">");
  44. }
  45. } else if (React.isValidElement(child)) {
  46. if (keepArray.indexOf(child.type) > -1 && Object.keys(child.props).length === 0) {
  47. stringNode = "".concat(stringNode, "<").concat(child.type, "/>");
  48. } else {
  49. stringNode = "".concat(stringNode, "<").concat(elementKey, "></").concat(elementKey, ">");
  50. }
  51. } else if (_typeof(child) === 'object') {
  52. var clone = _objectSpread({}, child);
  53. var format = clone.format;
  54. delete clone.format;
  55. var keys = Object.keys(clone);
  56. if (format && keys.length === 1) {
  57. stringNode = "".concat(stringNode, "{{").concat(keys[0], ", ").concat(format, "}}");
  58. } else if (keys.length === 1) {
  59. stringNode = "".concat(stringNode, "{{").concat(keys[0], "}}");
  60. } else {
  61. // not a valid interpolation object (can only contain one value plus format)
  62. warn("react-i18next: the passed in object contained more than one variable - the object should look like {{ value, format }} where format is optional.", child);
  63. }
  64. } else {
  65. 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);
  66. }
  67. });
  68. return stringNode;
  69. }
  70. function renderNodes(children, targetString, i18n, i18nOptions, combinedTOpts) {
  71. if (targetString === '') return []; // check if contains tags we need to replace from html string to react nodes
  72. var keepArray = i18nOptions.transKeepBasicHtmlNodesFor || [];
  73. var emptyChildrenButNeedsHandling = targetString && new RegExp(keepArray.join('|')).test(targetString); // no need to replace tags in the targetstring
  74. if (!children && !emptyChildrenButNeedsHandling) return [targetString]; // v2 -> interpolates upfront no need for "some <0>{{var}}</0>"" -> will be just "some {{var}}" in translation file
  75. var data = {};
  76. function getData(childs) {
  77. var childrenArray = getAsArray(childs);
  78. childrenArray.forEach(function (child) {
  79. if (typeof child === 'string') return;
  80. if (hasChildren(child)) getData(getChildren(child));else if (_typeof(child) === 'object' && !React.isValidElement(child)) Object.assign(data, child);
  81. });
  82. }
  83. getData(children);
  84. var interpolatedString = i18n.services.interpolator.interpolate(targetString, _objectSpread({}, data, {}, combinedTOpts), i18n.language); // parse ast from string with additional wrapper tag
  85. // -> avoids issues in parser removing prepending text nodes
  86. var ast = HTML.parse("<0>".concat(interpolatedString, "</0>"));
  87. function mapAST(reactNode, astNode) {
  88. var reactNodes = getAsArray(reactNode);
  89. var astNodes = getAsArray(astNode);
  90. return astNodes.reduce(function (mem, node, i) {
  91. var translationContent = node.children && node.children[0] && node.children[0].content;
  92. if (node.type === 'tag') {
  93. var child = reactNodes[parseInt(node.name, 10)] || {};
  94. var isElement = React.isValidElement(child);
  95. if (typeof child === 'string') {
  96. mem.push(child);
  97. } else if (hasChildren(child)) {
  98. var childs = getChildren(child);
  99. var mappedChildren = mapAST(childs, node.children);
  100. var inner = hasValidReactChildren(childs) && mappedChildren.length === 0 ? childs : mappedChildren;
  101. if (child.dummy) child.children = inner; // needed on preact!
  102. mem.push(React.cloneElement(child, _objectSpread({}, child.props, {
  103. key: i
  104. }), inner));
  105. } else if (emptyChildrenButNeedsHandling && _typeof(child) === 'object' && child.dummy && !isElement) {
  106. // we have a empty Trans node (the dummy element) with a targetstring that contains html tags needing
  107. // conversion to react nodes
  108. // so we just need to map the inner stuff
  109. var _inner = mapAST(reactNodes
  110. /* wrong but we need something */
  111. , node.children);
  112. mem.push(React.cloneElement(child, _objectSpread({}, child.props, {
  113. key: i
  114. }), _inner));
  115. } else if (Number.isNaN(parseFloat(node.name))) {
  116. if (i18nOptions.transSupportBasicHtmlNodes && keepArray.indexOf(node.name) > -1) {
  117. if (node.voidElement) {
  118. mem.push(React.createElement(node.name, {
  119. key: "".concat(node.name, "-").concat(i)
  120. }));
  121. } else {
  122. var _inner2 = mapAST(reactNodes
  123. /* wrong but we need something */
  124. , node.children);
  125. mem.push(React.createElement(node.name, {
  126. key: "".concat(node.name, "-").concat(i)
  127. }, _inner2));
  128. }
  129. } else if (node.voidElement) {
  130. mem.push("<".concat(node.name, " />"));
  131. } else {
  132. var _inner3 = mapAST(reactNodes
  133. /* wrong but we need something */
  134. , node.children);
  135. mem.push("<".concat(node.name, ">").concat(_inner3, "</").concat(node.name, ">"));
  136. }
  137. } else if (_typeof(child) === 'object' && !isElement) {
  138. var content = node.children[0] ? translationContent : null; // v1
  139. // as interpolation was done already we just have a regular content node
  140. // in the translation AST while having an object in reactNodes
  141. // -> push the content no need to interpolate again
  142. if (content) mem.push(content);
  143. } else if (node.children.length === 1 && translationContent) {
  144. // If component does not have children, but translation - has
  145. // with this in component could be components={[<span class='make-beautiful'/>]} and in translation - 'some text <0>some highlighted message</0>'
  146. mem.push(React.cloneElement(child, _objectSpread({}, child.props, {
  147. key: i
  148. }), translationContent));
  149. } else {
  150. mem.push(React.cloneElement(child, _objectSpread({}, child.props, {
  151. key: i
  152. })));
  153. }
  154. } else if (node.type === 'text') {
  155. mem.push(node.content);
  156. }
  157. return mem;
  158. }, []);
  159. } // call mapAST with having react nodes nested into additional node like
  160. // we did for the string ast from translation
  161. // return the children of that extra node to get expected result
  162. var result = mapAST([{
  163. dummy: true,
  164. children: children
  165. }], ast);
  166. return getChildren(result[0]);
  167. }
  168. export function Trans(_ref) {
  169. var children = _ref.children,
  170. count = _ref.count,
  171. parent = _ref.parent,
  172. i18nKey = _ref.i18nKey,
  173. tOptions = _ref.tOptions,
  174. values = _ref.values,
  175. defaults = _ref.defaults,
  176. components = _ref.components,
  177. ns = _ref.ns,
  178. i18nFromProps = _ref.i18n,
  179. tFromProps = _ref.t,
  180. additionalProps = _objectWithoutProperties(_ref, ["children", "count", "parent", "i18nKey", "tOptions", "values", "defaults", "components", "ns", "i18n", "t"]);
  181. var _ref2 = getHasUsedI18nextProvider() ? useContext(I18nContext) || {} : {},
  182. i18nFromContext = _ref2.i18n,
  183. defaultNSFromContext = _ref2.defaultNS;
  184. var i18n = i18nFromProps || i18nFromContext || getI18n();
  185. if (!i18n) {
  186. warnOnce('You will need pass in an i18next instance by using i18nextReactModule');
  187. return children;
  188. }
  189. var t = tFromProps || i18n.t.bind(i18n) || function (k) {
  190. return k;
  191. };
  192. var reactI18nextOptions = _objectSpread({}, getDefaults(), {}, i18n.options && i18n.options.react);
  193. var useAsParent = parent !== undefined ? parent : reactI18nextOptions.defaultTransParent; // prepare having a namespace
  194. var namespaces = ns || t.ns || defaultNSFromContext || i18n.options && i18n.options.defaultNS;
  195. namespaces = typeof namespaces === 'string' ? [namespaces] : namespaces || ['translation'];
  196. var defaultValue = defaults || nodesToString('', children, 0, reactI18nextOptions) || reactI18nextOptions.transEmptyNodeValue;
  197. var hashTransKey = reactI18nextOptions.hashTransKey;
  198. var key = i18nKey || (hashTransKey ? hashTransKey(defaultValue) : defaultValue);
  199. var interpolationOverride = values ? {} : {
  200. interpolation: {
  201. prefix: '#$?',
  202. suffix: '?$#'
  203. }
  204. };
  205. var combinedTOpts = _objectSpread({}, tOptions, {
  206. count: count
  207. }, values, {}, interpolationOverride, {
  208. defaultValue: defaultValue,
  209. ns: namespaces
  210. });
  211. var translation = key ? t(key, combinedTOpts) : defaultValue;
  212. if (!useAsParent) return renderNodes(components || children, translation, i18n, reactI18nextOptions, combinedTOpts);
  213. return React.createElement(useAsParent, additionalProps, renderNodes(components || children, translation, i18n, reactI18nextOptions, combinedTOpts));
  214. }