diff --git a/package-lock.json b/package-lock.json
index 2b1752af..c692b862 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,6 @@
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.14.5",
- "html-escaper": "^2.0.2",
"html-parse-stringify": "^3.0.1"
},
"devDependencies": {
@@ -7888,7 +7887,8 @@
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
},
"node_modules/html-parse-stringify": {
"version": "3.0.1",
@@ -17684,8 +17684,7 @@
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
- "dev": true,
- "requires": {}
+ "dev": true
},
"acorn-walk": {
"version": "6.2.0",
@@ -18171,8 +18170,7 @@
"version": "7.0.0-bridge.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
"integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
- "dev": true,
- "requires": {}
+ "dev": true
},
"babel-eslint": {
"version": "10.1.0",
@@ -21345,7 +21343,8 @@
"html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
},
"html-parse-stringify": {
"version": "3.0.1",
@@ -22956,8 +22955,7 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
"integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
- "dev": true,
- "requires": {}
+ "dev": true
},
"jest-regex-util": {
"version": "24.9.0",
diff --git a/package.json b/package.json
index ac522142..54bb989a 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,6 @@
},
"dependencies": {
"@babel/runtime": "^7.14.5",
- "html-escaper": "^2.0.2",
"html-parse-stringify": "^3.0.1"
},
"devDependencies": {
diff --git a/src/Trans.js b/src/Trans.js
index 155804ac..86fc443b 100644
--- a/src/Trans.js
+++ b/src/Trans.js
@@ -1,6 +1,5 @@
import { useContext, isValidElement, cloneElement, createElement } from 'react';
import HTML from 'html-parse-stringify';
-import { unescape } from 'html-escaper';
import { getI18n, I18nContext, getDefaults } from './context';
import { warn, warnOnce } from './utils';
@@ -124,8 +123,7 @@ function renderNodes(children, targetString, i18n, i18nOptions, combinedTOpts, s
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);
+ else if (typeof child === 'object' && !isValidElement(child)) Object.assign(data, child);
});
}
@@ -249,7 +247,9 @@ function renderNodes(children, targetString, i18n, i18nOptions, combinedTOpts, s
} else if (node.type === 'text') {
const wrapTextNodes = i18nOptions.transWrapTextNodes;
const content = shouldUnescape
- ? unescape(i18n.services.interpolator.interpolate(node.content, opts, i18n.language))
+ ? 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));
diff --git a/src/context.js b/src/context.js
index 5efd1457..d33664ad 100644
--- a/src/context.js
+++ b/src/context.js
@@ -1,4 +1,5 @@
import { createContext } from 'react';
+import { unescape } from './unescape';
let defaultOptions = {
bindI18n: 'languageChanged',
@@ -10,6 +11,7 @@ let defaultOptions = {
transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'],
// hashTransKey: key => key // calculate a key for Trans component based on defaultValue
useSuspense: true,
+ unescape,
};
let i18nInstance;
diff --git a/src/unescape.js b/src/unescape.js
new file mode 100644
index 00000000..1b01a2b1
--- /dev/null
+++ b/src/unescape.js
@@ -0,0 +1,18 @@
+const matchHtmlEntity = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g;
+
+const htmlEntities = {
+ '&': '&',
+ '&': '&',
+ '<': '<',
+ '<': '<',
+ '>': '>',
+ '>': '>',
+ ''': "'",
+ ''': "'",
+ '"': '"',
+ '"': '"',
+};
+
+const unescapeHtmlEntity = (m) => htmlEntities[m];
+
+export const unescape = (text) => text.replace(matchHtmlEntity, unescapeHtmlEntity);
diff --git a/test/i18n.js b/test/i18n.js
index 5ce3f156..d1237627 100644
--- a/test/i18n.js
+++ b/test/i18n.js
@@ -43,6 +43,7 @@ i18n.init({
transTest3_overwrite:
'Result should be a clickable link <0 href="https://www.google.com">Google0>',
transTestEscapedHtml: 'Escaped html should unescape correctly <0>< &>0>.',
+ transTestCustomUnescape: 'Text should be passed through custom unescape <0>0>',
testTransWithCtx: 'Go <1>there1>.',
testTransWithCtx_home: 'Go <1>home1>.',
deepPath: {
diff --git a/test/trans.render.spec.js b/test/trans.render.spec.js
index 3ff288fa..ec9cb915 100644
--- a/test/trans.render.spec.js
+++ b/test/trans.render.spec.js
@@ -644,6 +644,35 @@ describe('trans should allow escaped html', () => {
});
});
+describe('trans with custom unescape', () => {
+ let orgValue;
+ beforeAll(() => {
+ orgValue = i18n.options.react.unescape;
+ i18n.options.react.unescape = (text) => text.replace('', '\u00AD');
+ });
+
+ afterAll(() => {
+ i18n.options.react.unescape = orgValue;
+ });
+
+ it('should allow unescape override', () => {
+ const TestComponent = () => (
+