From 48e4d8b5694be96ded7a6f481d0280684a325790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Fri, 23 Oct 2020 13:40:39 +0200 Subject: [PATCH] Addining initial 18n support --- webapp/i18n/en.json | 11 + webapp/i18n/es.json | 11 + webapp/package-lock.json | 337 ++++++++++++++++++- webapp/package.json | 6 +- webapp/src/app.tsx | 56 +-- webapp/src/components/sidebar.tsx | 135 ++++++-- webapp/src/components/workspaceComponent.tsx | 4 +- webapp/src/i18n.tsx | 25 ++ webapp/src/pages/boardPage.tsx | 2 + webapp/webpack.common.js | 16 +- 10 files changed, 539 insertions(+), 64 deletions(-) create mode 100644 webapp/i18n/en.json create mode 100644 webapp/i18n/es.json create mode 100644 webapp/src/i18n.tsx diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json new file mode 100644 index 000000000..9b73f60fc --- /dev/null +++ b/webapp/i18n/en.json @@ -0,0 +1,11 @@ +{ + "Sidebar.add-board": "+ Add Board", + "Sidebar.delete-board": "Delete Board", + "Sidebar.export-archive": "Export Archive", + "Sidebar.import-archive": "Import Archive", + "Sidebar.set-english-language": "Set English Language", + "Sidebar.set-spanish-language": "Set Spanish Language", + "Sidebar.settings": "Settings", + "Sidebar.untitled-board": "(Untitled Board)", + "Sidebar.untitled-view": "(Untitled View)" +} \ No newline at end of file diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json new file mode 100644 index 000000000..24123e3c9 --- /dev/null +++ b/webapp/i18n/es.json @@ -0,0 +1,11 @@ +{ + "Sidebar.add-board": "+ Añadir Panel", + "Sidebar.delete-board": "Borrar Panel", + "Sidebar.export-archive": "Exportar Archivo", + "Sidebar.import-archive": "Importar Archivo", + "Sidebar.set-english-language": "Usar idioma Inglés", + "Sidebar.set-spanish-language": "Usar idioma Español", + "Sidebar.settings": "Configuración", + "Sidebar.untitled-board": "(Panel sin titulo)", + "Sidebar.untitled-view": "(Vista sin titulo)" +} diff --git a/webapp/package-lock.json b/webapp/package-lock.json index fe8e5e39d..219dd5a73 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -462,6 +462,157 @@ } } }, + "@formatjs/cli": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-2.13.2.tgz", + "integrity": "sha512-o0jY7AmaXIHugiV35xhKeNYQ9pzTi0O+stfZqY0+Sqb3MFk6oLz/K5ExvnXHvf3soiCOat4UhzAxHSR9T0QWwQ==", + "dev": true, + "requires": { + "@formatjs/ts-transformer": "^2.11.3", + "@types/json-stable-stringify": "^1.0.32", + "@types/lodash": "^4.14.150", + "@types/loud-rejection": "^2.0.0", + "@types/node": "14", + "chalk": "^4.0.0", + "commander": "^6.1.0", + "fast-glob": "^3.2.4", + "fs-extra": "^9.0.0", + "intl-messageformat-parser": "^6.0.10", + "json-stable-stringify": "^1.0.1", + "lodash": "^4.17.15", + "loud-rejection": "^2.2.0", + "tslib": "^2.0.1", + "typescript": "^4.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "commander": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", + "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "dev": true + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, + "@formatjs/ecma402-abstract": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.2.5.tgz", + "integrity": "sha512-k0fqS3LBNOHueAoMdgig8Ni6TchsH+zbzWBzX2gTFm50X9mxHwnuXdCk0XLlCIbvgVVlzcO254Men/mHAheMbg==", + "requires": { + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, + "@formatjs/intl": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-1.3.6.tgz", + "integrity": "sha512-LObg69JtDEZ8+Cqsu10h7AipKrsGpWd5xspIBJjWeQL+U0hTZ/f2PTNf/M5iOH8XRqc8ljCFGkaWn9bZGlmkaw==", + "requires": { + "@formatjs/ecma402-abstract": "^1.2.5", + "@formatjs/intl-displaynames": "^3.3.12", + "@formatjs/intl-listformat": "^4.2.10", + "@formatjs/intl-relativetimeformat": "^7.2.10", + "fast-memoize": "^2.5.2", + "intl-messageformat": "^9.3.11", + "intl-messageformat-parser": "^6.0.10", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, + "@formatjs/intl-displaynames": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-3.3.12.tgz", + "integrity": "sha512-otxSrw0Wmv3qbyes/hsI4+tlg6V0dSezhusUJHyegod4dG8U9q0C6gawgDlIWrBANl+VPNvcLooJFPHhG6ni7w==", + "requires": { + "@formatjs/ecma402-abstract": "^1.2.5", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, + "@formatjs/intl-listformat": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-4.2.10.tgz", + "integrity": "sha512-Wa9BaZnEg/km45Wj5lbRjFV89YHC/3P2ucsvk9lQklF6Kt7ybfTH6QDwITrlxo2EGvmcKPhxuBNHgrUhlxm9PQ==", + "requires": { + "@formatjs/ecma402-abstract": "^1.2.5", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, + "@formatjs/intl-relativetimeformat": { + "version": "7.2.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-7.2.10.tgz", + "integrity": "sha512-3eKfhK0X5O8JkJ9bSgkWCG9fXjFPQCxw18DWqhbZ5thTU2aKejpxBzwFYCo+o5TCIEmtn6G835nBqb2d197Gnw==", + "requires": { + "@formatjs/ecma402-abstract": "^1.2.5", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, + "@formatjs/ts-transformer": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.11.3.tgz", + "integrity": "sha512-nt51rmoeDnORFXGkJGX3tpC1oYY6WDkJTpOCCsYkRxWxmgY1kT5v8wckNB/CvBmz4egFNShr1iRNDa7o+EsKZA==", + "dev": true, + "requires": { + "intl-messageformat-parser": "^6.0.10", + "tslib": "^2.0.1", + "typescript": "^4.0" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1183,6 +1334,15 @@ "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==", "dev": true }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", @@ -1230,12 +1390,33 @@ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, + "@types/json-stable-stringify": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz", + "integrity": "sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/lodash": { + "version": "4.14.162", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.162.tgz", + "integrity": "sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig==", + "dev": true + }, + "@types/loud-rejection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/loud-rejection/-/loud-rejection-2.0.0.tgz", + "integrity": "sha512-oTHISsIybJGoh3b3Ay/10csbAd2k0su7G7DGrE1QWciC+IdydPm0WMw1+Gr9YMYjPiJ5poB3g5Ev73IlLoavLw==", + "dev": true, + "requires": { + "loud-rejection": "*" + } + }, "@types/marked": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-1.1.0.tgz", @@ -1263,14 +1444,12 @@ "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", - "dev": true + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "@types/react": { "version": "16.9.52", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.52.tgz", "integrity": "sha512-EHRjmnxiNivwhGdMh9sz1Yw9AUxTSZFxKqdBWAAzyZx3sufWwx6ogqHYh/WB1m/I4ZpjkoZLExF5QTy2ekVi/Q==", - "dev": true, "requires": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -1861,6 +2040,12 @@ "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", "dev": true }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, "array-includes": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", @@ -1992,6 +2177,12 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -3040,8 +3231,16 @@ "csstype": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", - "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==", - "dev": true + "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } }, "cyclist": { "version": "1.0.1", @@ -4492,6 +4691,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==" + }, "fastq": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", @@ -4851,6 +5055,18 @@ } } }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -5472,6 +5688,39 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, + "intl-messageformat": { + "version": "9.3.11", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.3.11.tgz", + "integrity": "sha512-eqnfSDa6oI/bBtPgzSAbsjUDUshhdLmMKOQds0xGhpcgp760Gq/Fk+f4HquO7VIGp9wSx7ej65e+NOJQTBe/YQ==", + "requires": { + "fast-memoize": "^2.5.2", + "intl-messageformat-parser": "^6.0.10", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, + "intl-messageformat-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.10.tgz", + "integrity": "sha512-xURB8PchDsdLEpedzPLzBmGCK996FPBl3yLQIUxcM3EAA3TdhXPArX6GY4LVXQNeq8V4WtwmgxaJOKtdOpA8pg==", + "requires": { + "@formatjs/ecma402-abstract": "^1.2.5", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -7179,6 +7428,12 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "dev": true + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -7203,6 +7458,15 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -7224,6 +7488,22 @@ "minimist": "^1.2.5" } }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -7373,6 +7653,16 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "loud-rejection": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-2.2.0.tgz", + "integrity": "sha512-S0FayMXku80toa5sZ6Ro4C+s+EtFDCsyJNG/AzFMfX3AxD5Si4dZsgzm/kKnbOxHl5Cv8jBlno8+3XYIh2pNjQ==", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.2" + } + }, "lower-case": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", @@ -8775,6 +9065,32 @@ "scheduler": "^0.19.1" } }, + "react-intl": { + "version": "5.8.6", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.8.6.tgz", + "integrity": "sha512-+TYAKYAVh1XPhm0ZxZ+LBUIdpjMe3Beit1928jrxaHk6SY0wyf20Qt14/s2sAkM5+FbKy+72pohC+Pqt6Ixvkg==", + "requires": { + "@formatjs/ecma402-abstract": "^1.2.5", + "@formatjs/intl": "^1.3.6", + "@formatjs/intl-displaynames": "^3.3.12", + "@formatjs/intl-listformat": "^4.2.10", + "@formatjs/intl-relativetimeformat": "^7.2.10", + "@types/hoist-non-react-statics": "^3.3.1", + "fast-memoize": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "^9.3.11", + "intl-messageformat-parser": "^6.0.10", + "shallow-equal": "^1.2.1", + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -9447,6 +9763,11 @@ "kind-of": "^6.0.2" } }, + "shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -10737,6 +11058,12 @@ "imurmurhash": "^0.1.4" } }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", diff --git a/webapp/package.json b/webapp/package.json index 9b0a32330..0fc3bb77a 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -9,12 +9,14 @@ "watchdev": "NODE_ENV=dev webpack --watch --config webpack.dev.js", "test": "jest", "check": "eslint --ext .tsx,.ts . --quiet --cache", - "fix": "eslint --ext .tsx,.ts . --quiet --fix --cache" + "fix": "eslint --ext .tsx,.ts . --quiet --fix --cache", + "i18n-extract": "formatjs extract src/**/*.tsx src/**/*.ts --out-file i18n/tmp.json; formatjs compile i18n/tmp.json --out-file i18n/en.json; rm i18n/tmp.json" }, "dependencies": { "marked": "^1.1.1", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-intl": "^5.8.6", "react-router-dom": "^5.2.0", "react-simplemde-editor": "^4.1.3" }, @@ -24,6 +26,8 @@ } }, "devDependencies": { + "@formatjs/cli": "^2.13.2", + "@formatjs/ts-transformer": "^2.11.3", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.0.4", "@types/jest": "^26.0.14", diff --git a/webapp/src/app.tsx b/webapp/src/app.tsx index ede42547e..ee94ba62a 100644 --- a/webapp/src/app.tsx +++ b/webapp/src/app.tsx @@ -1,39 +1,51 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from 'react' +import React, {useState} from 'react' +import {IntlProvider} from 'react-intl' import { BrowserRouter as Router, Switch, Route, - Link, } from 'react-router-dom' +import {getCurrentLanguage, getMessages, storeLanguage} from './i18n' + import LoginPage from './pages/loginPage' import BoardPage from './pages/boardPage' export default function App(): JSX.Element { + const [language, setLanguage] = useState(getCurrentLanguage()) + const setAndStoreLanguage = (lang: string) => { + storeLanguage(lang) + setLanguage(lang) + } return ( - -
-
- - - - - - - - - - - + + +
+
+ + + + + + + + + + + +
+ +
+ + - -
- - - + + ) } diff --git a/webapp/src/components/sidebar.tsx b/webapp/src/components/sidebar.tsx index 1474c0472..0f7779c86 100644 --- a/webapp/src/components/sidebar.tsx +++ b/webapp/src/components/sidebar.tsx @@ -1,6 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React from 'react' +import {FormattedMessage} from 'react-intl' import {Archiver} from '../archiver' import {Board, MutableBoard} from '../blocks/board' @@ -15,7 +16,8 @@ type Props = { showBoard: (id: string) => void showView: (id: string, boardId?: string) => void workspaceTree: WorkspaceTree, - boardTree?: BoardTree + boardTree?: BoardTree, + setLanguage: (lang: string) => void, } type State = { @@ -77,7 +79,12 @@ class Sidebar extends React.Component {
{ boards.map((board) => { - const displayTitle = board.title || '(Untitled Board)' + const displayTitle = board.title || ( + + ) const boardViews = views.filter((view) => view.parentId === board.id) return (
@@ -93,23 +100,30 @@ class Sidebar extends React.Component {
- { - const nextBoardId = boards.length > 1 ? boards.find((o) => o.id !== board.id).id : undefined - mutator.deleteBlock( - board, - 'delete block', - async () => { - nextBoardId && this.props.showBoard(nextBoardId!) - }, - async () => { - this.props.showBoard(board.id) - }, - ) - }} - /> + + {(text: string) => ( + { + const nextBoardId = boards.length > 1 ? boards.find((o) => o.id !== board.id).id : undefined + mutator.deleteBlock( + board, + 'delete block', + async () => { + nextBoardId && this.props.showBoard(nextBoardId!) + }, + async () => { + this.props.showBoard(board.id) + }, + ) + }} + /> + )} +
@@ -124,7 +138,12 @@ class Sidebar extends React.Component { this.viewClicked(board, view) }} > - {view.title || '(Untitled View)'} + {view.title || ( + + )}
) })} @@ -140,25 +159,73 @@ class Sidebar extends React.Component { onClick={() => { this.addBoardClicked() }} - >+ Add Board
+ > + +
-
Settings
+
+ +
- Archiver.importFullArchive(() => { - this.forceUpdate() - })} - /> - Archiver.exportFullArchive()} - /> + + {(text: string) => ( + Archiver.importFullArchive(() => { + this.forceUpdate() + })} + /> + )} + + + {(text: string) => ( + Archiver.exportFullArchive()} + /> + )} + + + {(text: string) => ( + this.props.setLanguage('en')} + /> + )} + + + {(text: string) => ( + this.props.setLanguage('es')} + /> + )} +
diff --git a/webapp/src/components/workspaceComponent.tsx b/webapp/src/components/workspaceComponent.tsx index e85d63cf5..620d37d2f 100644 --- a/webapp/src/components/workspaceComponent.tsx +++ b/webapp/src/components/workspaceComponent.tsx @@ -17,11 +17,12 @@ type Props = { showView: (id: string, boardId?: string) => void showFilter: (el: HTMLElement) => void setSearchText: (text: string) => void + setLanguage: (lang: string) => void } class WorkspaceComponent extends React.Component { render() { - const {boardTree, workspaceTree, showBoard, showView} = this.props + const {boardTree, workspaceTree, showBoard, showView, setLanguage} = this.props Utils.assert(workspaceTree) const element = @@ -31,6 +32,7 @@ class WorkspaceComponent extends React.Component { showView={showView} workspaceTree={workspaceTree} boardTree={boardTree} + setLanguage={setLanguage} /> {this.mainComponent()}
) diff --git a/webapp/src/i18n.tsx b/webapp/src/i18n.tsx new file mode 100644 index 000000000..d751b20f3 --- /dev/null +++ b/webapp/src/i18n.tsx @@ -0,0 +1,25 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import messages_en from '../i18n/en.json' +import messages_es from '../i18n/es.json' + +export function getMessages(lang: string): {[key: string]: string} { + switch (lang) { + case 'es': + return messages_es + } + return messages_en +} + +export function getCurrentLanguage(): string { + let lang = localStorage.getItem('language') + if (!lang) { + lang = navigator.language.split(/[-_]/)[0] + } + return lang +} + +export function storeLanguage(lang: string): void { + localStorage.setItem('language', lang) +} diff --git a/webapp/src/pages/boardPage.tsx b/webapp/src/pages/boardPage.tsx index fd5facdd4..fcc874364 100644 --- a/webapp/src/pages/boardPage.tsx +++ b/webapp/src/pages/boardPage.tsx @@ -14,6 +14,7 @@ import {Utils} from '../utils' import {MutableWorkspaceTree} from '../viewModel/workspaceTree' type Props = { + setLanguage: (lang: string) => void } type State = { @@ -157,6 +158,7 @@ export default class BoardPage extends React.Component { setSearchText={(text) => { this.setSearchText(text) }} + setLanguage={this.props.setLanguage} /> ) diff --git a/webapp/webpack.common.js b/webapp/webpack.common.js index 9d8505907..f066a38dc 100644 --- a/webapp/webpack.common.js +++ b/webapp/webpack.common.js @@ -1,5 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +const tsTransformer = require('@formatjs/ts-transformer'); const path = require('path'); const CopyPlugin = require('copy-webpack-plugin'); var HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -18,8 +19,21 @@ function makeCommonConfig() { rules: [ { test: /\.tsx?$/, - use: 'ts-loader', + use: { + loader: 'ts-loader', + options: { + getCustomTransformers: { + before: [ + tsTransformer.transform({ + overrideIdFn: '[sha512:contenthash:base64:6]', + ast: true, + }), + ], + }, + }, + }, exclude: [/node_modules/], + }, { test: /\.html$/,