From 9e2ab558b5483ce1527df965ec76d8d1095b5b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 3 Jun 2024 17:40:35 +0200 Subject: [PATCH] Adding i18n support to the frontend (#194) * Adding frontend translations * More translated blocks * Translations done and added the spanish translations * Removing unneeded dependency * Fixing linter errors * Translating also the suggested items in the copilot * Reviewing spanish ortography * Adding new translations * Simplify translations initialization * Using random generated ids for translations * Making the formatted message oneliners whenever makes sense * Some extra code styling changes * Fixing problem of messages without id * Adding the new translations * Polishing spanish translation --- Makefile | 8 +- webapp/babel.config.js | 7 + webapp/package-lock.json | 300 +++++++++++++++++- webapp/package.json | 3 + webapp/src/components/bot_slector.tsx | 5 +- webapp/src/components/dropdown_info.tsx | 3 +- webapp/src/components/llmbot_post.tsx | 9 +- webapp/src/components/post_menu.tsx | 14 +- webapp/src/components/postback_post.tsx | 9 +- webapp/src/components/rhs/rhs.tsx | 18 +- webapp/src/components/rhs/rhs_header.tsx | 10 +- webapp/src/components/rhs/rhs_new_tab.tsx | 61 ++-- .../src/components/system_console/avatar.tsx | 5 +- webapp/src/components/system_console/bot.tsx | 37 ++- webapp/src/components/system_console/bots.tsx | 8 +- .../src/components/system_console/config.tsx | 80 +++-- webapp/src/components/system_console/item.tsx | 5 +- webapp/src/components/unreads_summarize.tsx | 11 +- webapp/src/i18n/en.json | 81 +++++ webapp/src/i18n/es.json | 81 +++++ webapp/src/index.tsx | 14 +- webapp/webpack.config.js | 5 + 22 files changed, 650 insertions(+), 124 deletions(-) create mode 100644 webapp/src/i18n/en.json create mode 100644 webapp/src/i18n/es.json diff --git a/Makefile b/Makefile index 1d6a7744..204cd662 100644 --- a/Makefile +++ b/Makefile @@ -234,13 +234,7 @@ endif ## Extract strings for translation from the source code. .PHONY: i18n-extract i18n-extract: -ifneq ($(HAS_WEBAPP),) -ifeq ($(HAS_MM_UTILITIES),) - @echo "You must clone github.com/mattermost/mattermost-utilities repo in .. to use this command" -else - cd $(MM_UTILITIES_DIR) && npm install && npm run babel && node mmjstool/build/index.js i18n extract-webapp --webapp-dir $(PWD)/webapp -endif -endif + cd webapp && $(NPM) run i18n-extract -- --out-file src/i18n/en.json --id-interpolation-pattern '[sha512:contenthash:base64:8]' --format simple src/index.tsx src/components/**/*.{ts,tsx} ## Install NPM dependencies for e2e tests e2e/node_modules: e2e/package.json diff --git a/webapp/babel.config.js b/webapp/babel.config.js index ebc51c2f..bfa13387 100644 --- a/webapp/babel.config.js +++ b/webapp/babel.config.js @@ -32,6 +32,13 @@ const config = { fileName: false, }, ], + [ + 'formatjs', + { + idInterpolationPattern: '[sha512:contenthash:base64:8]', + ast: true, + }, + ], ], }; diff --git a/webapp/package-lock.json b/webapp/package-lock.json index dcf1db19..938e1b47 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -13,6 +13,7 @@ "process": "0.11.10", "react": "^16.14.0", "react-dom": "^16.14.0", + "react-intl": "5.25.1", "react-redux": "8.0.5", "react-use": "17.4.0", "redux": "4.2.1", @@ -26,6 +27,7 @@ "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "7.21.5", "@babel/runtime": "7.21.5", + "@formatjs/cli": "6.2.12", "@mattermost/types": "7.10.0", "@types/babel__core": "7.20.0", "@types/babel__template": "7.4.1", @@ -2002,6 +2004,140 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==" }, + "node_modules/@formatjs/cli": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-6.2.12.tgz", + "integrity": "sha512-bt1NEgkeYN8N9zWcpsPu3fZ57vv+biA+NtIQBlyOZnCp1bcvh+vNTXvmwF4C5qxqDtCylpOIb3yi3Ktgp4v0JQ==", + "dev": true, + "bin": { + "formatjs": "bin/formatjs" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "@glimmer/env": "^0.1.7", + "@glimmer/reference": "^0.91.1 || ^0.92.0", + "@glimmer/syntax": "^0.92.0", + "@glimmer/validator": "^0.92.0", + "@vue/compiler-core": "^3.4.0", + "content-tag": "^2.0.1", + "ember-template-recast": "^6.1.4", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "@glimmer/env": { + "optional": true + }, + "@glimmer/reference": { + "optional": true + }, + "@glimmer/syntax": { + "optional": true + }, + "@glimmer/validator": { + "optional": true + }, + "@vue/compiler-core": { + "optional": true + }, + "content-tag": { + "optional": true + }, + "ember-template-recast": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -3274,16 +3410,16 @@ } }, "node_modules/babel-plugin-formatjs/node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/babel-plugin-polyfill-corejs2": { @@ -5172,6 +5308,17 @@ "node": ">= 0.10" } }, + "node_modules/intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -6260,6 +6407,32 @@ "react": "^16.14.0" } }, + "node_modules/react-intl": { + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "^16.3.0 || 17 || 18", + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -8928,6 +9101,91 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==" }, + "@formatjs/cli": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-6.2.12.tgz", + "integrity": "sha512-bt1NEgkeYN8N9zWcpsPu3fZ57vv+biA+NtIQBlyOZnCp1bcvh+vNTXvmwF4C5qxqDtCylpOIb3yi3Ktgp4v0JQ==", + "dev": true, + "requires": {} + }, + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + } + }, + "@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + } + }, + "@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "requires": { + "tslib": "^2.1.0" + } + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -9919,9 +10177,9 @@ } }, "typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true } } @@ -11335,6 +11593,17 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, + "intl-messageformat": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + } + }, "is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -12127,6 +12396,23 @@ "scheduler": "^0.19.1" } }, + "react-intl": { + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/webapp/package.json b/webapp/package.json index b306b027..3bab349c 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -10,6 +10,7 @@ "test": "echo ''", "test:watch": "echo ''", "test-ci": "echo ''", + "i18n-extract": "formatjs extract", "check-types": "tsc" }, "devDependencies": { @@ -19,6 +20,7 @@ "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "7.21.5", "@babel/runtime": "7.21.5", + "@formatjs/cli": "6.2.12", "@mattermost/types": "7.10.0", "@types/babel__core": "7.20.0", "@types/babel__template": "7.4.1", @@ -62,6 +64,7 @@ "process": "0.11.10", "react": "^16.14.0", "react-dom": "^16.14.0", + "react-intl": "5.25.1", "react-redux": "8.0.5", "react-use": "17.4.0", "redux": "4.2.1", diff --git a/webapp/src/components/bot_slector.tsx b/webapp/src/components/bot_slector.tsx index 51596717..d5a161ea 100644 --- a/webapp/src/components/bot_slector.tsx +++ b/webapp/src/components/bot_slector.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import {FormattedMessage} from 'react-intl'; import styled from 'styled-components'; @@ -27,7 +28,7 @@ export const DropdownBotSelector = (props: DropdownBotSelectorProps) => { > <> - {'Generate With:'} + {props.activeBot?.displayName} @@ -71,7 +72,7 @@ export const BotDropdown = (props: BotDropdownProps) => { dropdownMenu={StyledDropdownMenu} > - {'Choose a Bot'} + {props.bots.map((bot) => { const botProfileURL = getProfilePictureUrl(bot.id, bot.lastIconUpdate); diff --git a/webapp/src/components/dropdown_info.tsx b/webapp/src/components/dropdown_info.tsx index 2c39b4c0..c71ab776 100644 --- a/webapp/src/components/dropdown_info.tsx +++ b/webapp/src/components/dropdown_info.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import {FormattedMessage} from 'react-intl'; import styled from 'styled-components'; import {LightbulbOutlineIcon} from '@mattermost/compass-icons/components'; @@ -37,7 +38,7 @@ export const DropdownInfoOnlyVisibleToYou = () => { return ( - {'Copilot posts responses in the right panel which will only be visible to you.'} + ); }; diff --git a/webapp/src/components/llmbot_post.tsx b/webapp/src/components/llmbot_post.tsx index 51e27d89..d518aeb6 100644 --- a/webapp/src/components/llmbot_post.tsx +++ b/webapp/src/components/llmbot_post.tsx @@ -1,4 +1,5 @@ import React, {MouseEvent, useEffect, useRef, useState} from 'react'; +import {FormattedMessage} from 'react-intl'; import {useSelector} from 'react-redux'; import styled, {css, createGlobalStyle} from 'styled-components'; @@ -245,12 +246,12 @@ export const LLMBotPost = (props: Props) => { onClick={stopGenerating} > - {'Stop Generating'} + } { showPostbackButton && - {'Would you like to post this summary to the original call thread? You can also ask Copilot to make changes.'} + } { showControlsBar && @@ -261,7 +262,7 @@ export const LLMBotPost = (props: Props) => { onClick={postSummary} > - {'Post summary'} + } { showRegenerate && @@ -270,7 +271,7 @@ export const LLMBotPost = (props: Props) => { onClick={regnerate} > - {'Regenerate'} + } diff --git a/webapp/src/components/post_menu.tsx b/webapp/src/components/post_menu.tsx index 93cc6ee2..0b0f0289 100644 --- a/webapp/src/components/post_menu.tsx +++ b/webapp/src/components/post_menu.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import {FormattedMessage, useIntl} from 'react-intl'; import {Post} from '@mattermost/types/posts'; @@ -25,6 +26,7 @@ type Props = { const PostMenu = (props: Props) => { const selectPost = useSelectPost(); + const intl = useIntl(); const {bots, activeBot, setActiveBot} = useBotlist(); const post = props.post; const isBasicsLicensed = useIsBasicsLicensed(); @@ -46,7 +48,7 @@ const PostMenu = (props: Props) => { return ( } - title='AI Actions' + title={intl.formatMessage({defaultMessage: 'AI Actions'})} dropdownMenu={StyledDropdownMenu} > { setActiveBot={setActiveBot} /> - summarizePost(post.id)}>{'Summarize Thread'} - doReaction(post.id)}>{'React for me'} + summarizePost(post.id)}> + + + + doReaction(post.id)}> + + + diff --git a/webapp/src/components/postback_post.tsx b/webapp/src/components/postback_post.tsx index 5df84696..5947f571 100644 --- a/webapp/src/components/postback_post.tsx +++ b/webapp/src/components/postback_post.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {useSelector} from 'react-redux'; +import {FormattedMessage} from 'react-intl'; import {GlobalState} from '@mattermost/types/store'; @@ -12,7 +13,6 @@ interface Props { export const PostbackPost = (props: Props) => { const editorUsername = useSelector((state) => state.entities.users.profiles[props.post.props.userid]?.username); const botUsername = useSelector((state) => state.entities.users.profiles[props.post.user_id]?.username); - const userMotificationMessage = 'This summary was created by ' + botUsername + ' then edited and posted by @' + editorUsername; return ( <> { postID={props.post.id} />
- {userMotificationMessage} + + + ); }; diff --git a/webapp/src/components/rhs/rhs.tsx b/webapp/src/components/rhs/rhs.tsx index 77f18624..7747633e 100644 --- a/webapp/src/components/rhs/rhs.tsx +++ b/webapp/src/components/rhs/rhs.tsx @@ -1,4 +1,5 @@ import React, {useState, useEffect, useCallback} from 'react'; +import {FormattedMessage, useIntl} from 'react-intl'; import {useDispatch, useSelector} from 'react-redux'; import styled from 'styled-components'; @@ -58,6 +59,7 @@ const twentyFourHoursInMS = 24 * 60 * 60 * 1000; export default function RHS() { const dispatch = useDispatch(); + const intl = useIntl(); const [currentTab, setCurrentTab] = useState('new'); const selectedPostId = useSelector((state: any) => state['plugins-' + manifest.id].selectedPostId); const currentUserId = useSelector((state) => state.entities.users.currentUserId); @@ -95,16 +97,16 @@ export default function RHS() { - {'Copilot is not yet configured for this workspace'} - {'A system admin needs to complete the configuration before it can be used.'} + + - {'What is Copilot?'} - {'Copilot is a plugin that enables you to leverage the power of AI to:'} + +
    - {'Get caught up quickly with instant summarization for channels and threads.'} - {'Create meeting summaries in a flash.'} - {'Ask Copilot anything to get quick answers.'} + + +
@@ -121,7 +123,7 @@ export default function RHS() { content = ( { if (props.currentTab === 'threads') { historyButton = ( - {'Chat history'} + + ); } else { @@ -36,7 +38,8 @@ const RHSHeader = (props: Props) => { props.selectPost(''); }} > - {'View chat history'} + + ); } @@ -53,7 +56,8 @@ const RHSHeader = (props: Props) => { props.selectPost(''); }} > - {'New chat'} + + )} {(props.currentTab === 'new' && props.bots) && ( diff --git a/webapp/src/components/rhs/rhs_new_tab.tsx b/webapp/src/components/rhs/rhs_new_tab.tsx index b245a588..19f1f636 100644 --- a/webapp/src/components/rhs/rhs_new_tab.tsx +++ b/webapp/src/components/rhs/rhs_new_tab.tsx @@ -1,5 +1,6 @@ -import React, {useState} from 'react'; +import React, {useState, useCallback} from 'react'; import styled from 'styled-components'; +import {useIntl, FormattedMessage} from 'react-intl'; import { FormatListNumberedIcon, @@ -72,41 +73,53 @@ const setEditorText = (text: string) => { } }; -const addBrainstormingIdeas = () => { - setEditorText('Brainstorm ideas about '); -}; - -const addMeetingAgenda = () => { - setEditorText('Write a meeting agenda about '); -}; - -const addToDoList = () => { - setEditorText('Write a todo list about '); -}; - -const addProsAndCons = () => { - setEditorText('Write a pros and cons list about '); -}; - const RHSNewTab = ({botChannelId, selectPost, setCurrentTab}: Props) => { const dispatch = useDispatch(); + const intl = useIntl(); const [draft, updateDraft] = useState(null); + const addBrainstormingIdeas = useCallback(() => { + setEditorText(intl.formatMessage({defaultMessage: 'Brainstorm ideas about '})); + }, []); + + const addMeetingAgenda = useCallback(() => { + setEditorText(intl.formatMessage({defaultMessage: 'Write a meeting agenda about '})); + }, []); + + const addToDoList = useCallback(() => { + setEditorText(intl.formatMessage({defaultMessage: 'Write a todo list about '})); + }, []); + + const addProsAndCons = useCallback(() => { + setEditorText(intl.formatMessage({defaultMessage: 'Write a pros and cons list about '})); + }, []); return ( - {'Ask Copilot anything'} - {'The Copilot is here to help. Choose from the prompts below or write your own.'} + + - {'Brainstorm ideas'} - {'Meeting agenda'} - {'±'}{'Pros and Cons'} - {'To-do list'} + + + + + + + + + + {'±'} + + + + + + { const post = {...p}; diff --git a/webapp/src/components/system_console/avatar.tsx b/webapp/src/components/system_console/avatar.tsx index 1a251db8..1d3b8967 100644 --- a/webapp/src/components/system_console/avatar.tsx +++ b/webapp/src/components/system_console/avatar.tsx @@ -1,5 +1,6 @@ import React, {ChangeEvent, useEffect, useRef, useState} from 'react'; import styled from 'styled-components'; +import {FormattedMessage} from 'react-intl'; //@ts-ignore it exists import aiIcon from 'src/../../assets/bot_icon.png'; @@ -47,7 +48,7 @@ const AvatarItem = (props: AvatarItemProps) => { return ( <> - {'Bot avatar'} + { accept='.jpeg,.jpg,.png,.gif' // From the MM server requirements onChange={onUploadChange} /> - {'Upload Image'} + diff --git a/webapp/src/components/system_console/bot.tsx b/webapp/src/components/system_console/bot.tsx index c415e9f3..93472516 100644 --- a/webapp/src/components/system_console/bot.tsx +++ b/webapp/src/components/system_console/bot.tsx @@ -1,5 +1,6 @@ import React, {useState} from 'react'; import styled from 'styled-components'; +import {FormattedMessage, useIntl} from 'react-intl'; import {TrashCanOutlineIcon, ChevronDownIcon, AlertOutlineIcon, ChevronUpIcon} from '@mattermost/compass-icons/components'; @@ -52,6 +53,7 @@ function serviceTypeToDisplayName(serviceType: string): string { const Bot = (props: Props) => { const [open, setOpen] = useState(false); + const intl = useIntl(); const missingInfo = props.bot.name === '' || props.bot.displayName === '' || props.bot.service.type === '' || @@ -74,7 +76,7 @@ const Bot = (props: Props) => { {missingInfo && ( - {'Missing information'} + )} { props.onChange({...props.bot, displayName: e.target.value})} /> props.onChange({...props.bot, name: e.target.value})} /> @@ -103,7 +105,7 @@ const Bot = (props: Props) => { changedAvatar={props.changedAvatar} /> props.onChange({...props.bot, service: {...props.bot.service, type: e.target.value}})} > @@ -117,18 +119,18 @@ const Bot = (props: Props) => { onChange={(service) => props.onChange({...props.bot, service})} /> props.onChange({...props.bot, customInstructions: e.target.value})} /> { (props.bot.service.type === 'openai' || props.bot.service.type === 'openaicompatible') && ( props.onChange({...props.bot, enableVision: to})} - helpText='Enable Vision to allow the bot to process images. Requires a compatible model.' + helpText={intl.formatMessage({defaultMessage: 'Enable Vision to allow the bot to process images. Requires a compatible model.'})} /> )} @@ -145,20 +147,21 @@ type ServiceItemProps = { const ServiceItem = (props: ServiceItemProps) => { const type = props.service.type; + const intl = useIntl(); const hasAPIKey = type !== 'asksage'; const isOpenAIType = type === 'openai' || type === 'openaicompatible'; return ( <> {type === 'openaicompatible' && ( props.onChange({...props.service, apiURL: e.target.value})} /> )} {hasAPIKey && ( props.onChange({...props.service, apiKey: e.target.value})} @@ -166,7 +169,7 @@ const ServiceItem = (props: ServiceItemProps) => { )} {isOpenAIType && ( props.onChange({...props.service, orgId: e.target.value})} /> @@ -174,30 +177,30 @@ const ServiceItem = (props: ServiceItemProps) => { {type === 'asksage' && ( <> props.onChange({...props.service, username: e.target.value})} /> props.onChange({...props.service, password: e.target.value})} /> )} props.onChange({...props.service, defaultModel: e.target.value})} /> props.onChange({...props.service, tokenLimit: parseInt(e.target.value, 10)})} /> {isOpenAIType && ( props.onChange({...props.service, streamingTimeoutSeconds: parseInt(e.target.value, 10)})} /> diff --git a/webapp/src/components/system_console/bots.tsx b/webapp/src/components/system_console/bots.tsx index 3fe1be1c..3fac55a9 100644 --- a/webapp/src/components/system_console/bots.tsx +++ b/webapp/src/components/system_console/bots.tsx @@ -1,6 +1,7 @@ import React from 'react'; import styled from 'styled-components'; import {PlusIcon} from '@mattermost/compass-icons/components'; +import {FormattedMessage, useIntl} from 'react-intl'; import {TertiaryButton} from '../assets/buttons'; @@ -44,6 +45,7 @@ type Props = { const Bots = (props: Props) => { const multiLLMLicensed = useIsMultiLLMLicensed(); const licenceAddDisabled = !multiLLMLicensed && props.bots.length > 0; + const intl = useIntl(); const addNewBot = (e: React.MouseEvent) => { e.preventDefault(); @@ -89,12 +91,12 @@ const Bots = (props: Props) => { disabled={licenceAddDisabled} > - {'Add an AI Bot'} + {licenceAddDisabled && ( )} diff --git a/webapp/src/components/system_console/config.tsx b/webapp/src/components/system_console/config.tsx index a43c18d8..7820bc71 100644 --- a/webapp/src/components/system_console/config.tsx +++ b/webapp/src/components/system_console/config.tsx @@ -1,5 +1,6 @@ import React, {useEffect, useState} from 'react'; import styled from 'styled-components'; +import {FormattedMessage, useIntl} from 'react-intl'; import {PlusIcon} from '@mattermost/compass-icons/components'; @@ -74,18 +75,20 @@ const defaultConfig = { const BetaMessage = () => ( - - {'BETA'} - + - {'This plugin is currently in beta. To report a bug or to provide feedback, '} - - {'create a new issue in the plugin repository'} - + ( + + {chunks} + + )}} + /> ); @@ -93,6 +96,7 @@ const BetaMessage = () => ( const Config = (props: Props) => { const value = props.value || defaultConfig; const [avatarUpdates, setAvatarUpdates] = useState<{[key: string]: File}>({}); + const intl = useIntl(); useEffect(() => { const save = async () => { @@ -134,8 +138,8 @@ const Config = (props: Props) => { { botChangedAvatar={botChangedAvatar} /> - {'AI services are third party services; Mattermost is not responsible for output.'} + { props.onChange(props.id, {...value, defaultBotName: e.target.value}); @@ -179,14 +183,14 @@ const Config = (props: Props) => {
-
{'Global flag for all below settings.'}
+
+ + + +
{value.enableUserRestrictions && ( @@ -216,7 +224,7 @@ const Config = (props: Props) => {
@@ -244,7 +254,7 @@ const Config = (props: Props) => { className='control-label col-sm-4' htmlFor='ai-allow-team-ids' > - {'Allow Team IDs (csv):'} +
{ className='control-label col-sm-4' htmlFor='ai-only-users-on-team' > - {'Only Users on Team:'} +
{
@@ -286,7 +296,7 @@ const Config = (props: Props) => { className='control-label col-sm-4' htmlFor='ai-service-name' > - {'Enable LLM Trace:'} +
-
{'Enable tracing of LLM requests. Outputs whole conversations to the logs.'}
+
+ + + +
diff --git a/webapp/src/components/system_console/item.tsx b/webapp/src/components/system_console/item.tsx index 679715c5..5b96395a 100644 --- a/webapp/src/components/system_console/item.tsx +++ b/webapp/src/components/system_console/item.tsx @@ -1,5 +1,6 @@ import React from 'react'; import styled from 'styled-components'; +import {FormattedMessage} from 'react-intl'; export const ItemList = styled.div` display: grid; @@ -128,14 +129,14 @@ export const BooleanItem = (props: BooleanItemProps) => { checked={props.value} onChange={() => props.onChange(true)} /> - {'true'} + props.onChange(false)} /> - {'false'} + {props.helpText && {props.helpText} diff --git a/webapp/src/components/unreads_summarize.tsx b/webapp/src/components/unreads_summarize.tsx index 65ca4b68..e57b0bae 100644 --- a/webapp/src/components/unreads_summarize.tsx +++ b/webapp/src/components/unreads_summarize.tsx @@ -1,5 +1,6 @@ import React from 'react'; import styled from 'styled-components'; +import {FormattedMessage} from 'react-intl'; import {useSelectPost} from '@/hooks'; @@ -108,7 +109,9 @@ const UnreadsSumarize = (props: Props) => { return ( {' Ask AI'}} + icon={<> + + } dropdownMenu={StyledDropdownMenu} > { onClick={summarizeNew} > - {'Summarize new messages'} + - {'Find action items'} + - {'Find open questions'} + diff --git a/webapp/src/i18n/en.json b/webapp/src/i18n/en.json new file mode 100644 index 00000000..940591f6 --- /dev/null +++ b/webapp/src/i18n/en.json @@ -0,0 +1,81 @@ +{ + "/0dS48cO": "Enable User Restrictions:", + "/dm2sj3W": "Reply...", + "0SC8eQgh": "This summary was created by {botUsername} then edited and posted by @{editorUsername}", + "16KWPQAm": "Default bot", + "1D4s4n/Y": "AI Functions", + "1F6GhT33": "Ask Copilot anything...", + "1Hi/TDH+": "Ask Copilot anything to get quick answers.", + "1lGmoRer": "Enable LLM Trace:", + "1xOt4zt+": "Copilot posts responses in the right panel which will only be visible to you.", + "4dZi3YBP": "API Key", + "5sg7KCrr": "Password", + "6PgVSeKg": "Regenerate", + "7q7HBxeR": "Choose a Bot", + "8JdTl0YV": "Enable Vision to allow the bot to process images. Requires a compatible model.", + "8xYxQUzK": "Find action items", + "ATDyLPIo": "New chat", + "AZfEIIEi": "Ask Copilot anything", + "Ac92FquY": "Multiple AI services is available on Enterprise plans", + "C3m9hkE2": "Stop Generating", + "D0La/m5Z": "Organization ID", + "D7U9ZoTL": "Custom instructions", + "E+1cIU54": "Summarize new messages", + "E/T8p1Gl": "A system admin needs to complete the configuration before it can be used.", + "E1J2uJ2l": "Ask AI", + "EEvZiHhB": "Brainstorm ideas about", + "FGTvbaty": "Would you like to post this summary to the original call thread? You can also ask Copilot to make changes.", + "HMUo+5uG": "Enable Vision", + "HOkdCgNn": "Token limit", + "HYbZtR6A": "Enable tracing of LLM requests. Outputs whole conversations to the logs.", + "HberkbyG": "React for me", + "HigwY2IC": "User restrictions (experimental)", + "Hn93Hjdt": "This plugin is currently in beta. To report a bug or to provide feedback, create a new issue in the plugin repository.", + "JCIgkjKX": "Username", + "JLL4ie2j": "AI Actions", + "Ku669Gj+": "Meeting agenda", + "LeYMnIU1": "Post summary", + "LskuXn8V": "Allow Private Channels:", + "MntrZeJt": "Upload Image", + "N1MjLfHK": "Allow Team IDs (csv):", + "Ncjgeg3G": "Write a meeting agenda about", + "OyOTNe+S": "Bot avatar", + "S24j7sXB": "Write a pros and cons list about", + "S9zhSWmI": "Missing information", + "TpaMMxR8": "Team members can mention this bot with this username", + "UvNpDP3m": "Pros and Cons", + "V4TGblFD": "Bot Username", + "VRUze7ht": "How would you like the AI to respond?", + "VfxfZ8Lf": "Enable restrictions to allow or not users to use AI in this instance.", + "XK7rtqla": "Write a todo list about", + "XaCdJb86": "Summarize Thread", + "YGyAkqd5": "Generate With:", + "YmXaPq7f": "AI services are third party services; Mattermost is not responsible for output.", + "Z17cukDt": "Chat history", + "aH3xyeJP": "Choose which bot you want to be the default for each function.", + "bV+YmcFC": "Default model", + "cTgKF+6f": "Only Users on Team:", + "cZ+mfu9J": "false", + "dOQCL8n7": "Display name", + "eMUupPIl": "Get caught up quickly with instant summarization for channels and threads.", + "eQUYygRa": "To-do list", + "eiVgJmO6": "Add an AI Bot", + "faKga4wz": "Streaming Timeout Seconds", + "gY19rcnT": "Find open questions", + "i04PqEZU": "Copilot is not yet configured for this workspace", + "jWHIuwto": "View chat history", + "kMoYLtG8": "The Copilot is here to help. Choose from the prompts below or write your own.", + "kSDNX67w": "true", + "l4dlHzot": "Copilot is a plugin that enables you to leverage the power of AI to:", + "lOgYVyAe": "API URL", + "n7yYXG7R": "Service", + "oLNF8HT5": "AI Bots", + "pvmoJR47": "What is Copilot?", + "sW9GShHD": "Global flag for all below settings.", + "uLBt7sJr": "Brainstorm ideas", + "uklLqD3r": "Use multiple AI bots on Enterprise plans", + "vroSRZd5": "BETA", + "yOs8epTG": "Multiple AI services can be configured below.", + "z3UjXRZw": "Debug", + "zrQ5LJLt": "Create meeting summaries in a flash." +} diff --git a/webapp/src/i18n/es.json b/webapp/src/i18n/es.json new file mode 100644 index 00000000..44a47b2e --- /dev/null +++ b/webapp/src/i18n/es.json @@ -0,0 +1,81 @@ +{ + "/0dS48cO": "Habilitar restricciones de usuario:", + "/dm2sj3W": "Responder...", + "0SC8eQgh": "Este resumen fue creado por {botUsername} y luego editado y publicad por @{editorUsername}", + "16KWPQAm": "Bot por defecto", + "1D4s4n/Y": "Funciones de IA", + "1F6GhT33": "Pregunta a Copilot lo que quieras...", + "1Hi/TDH+": "Preguntar a Copilot lo que quieras para conseguir respuestas rápidas.", + "1lGmoRer": "Habilitar trazas de LLM:", + "1xOt4zt+": "Copilot publica respuestas en el panel derecho que solo son visibles para ti.", + "4dZi3YBP": "Clave de la API", + "5sg7KCrr": "Contraseña", + "6PgVSeKg": "Regenerar", + "7q7HBxeR": "Elige un bot", + "8JdTl0YV": "Habilitar visión permite al bot procesar imágenes. Requiere un modelo compatible.", + "8xYxQUzK": "Encontrar tareas", + "ATDyLPIo": "Nuevo conversación", + "AZfEIIEi": "Pregunta a Copilot lo que quieras", + "Ac92FquY": "Múltiples servicios de IA están disponibles en el plan Enterprise", + "C3m9hkE2": "Parar generación", + "D0La/m5Z": "ID de organización", + "D7U9ZoTL": "Instrucciones personalizadas", + "E+1cIU54": "Resume nuevos mensajes", + "E/T8p1Gl": "Un adminsitrador del sistema tiene que completar la configuración antes de que pueda ser usado.", + "E1J2uJ2l": "Pregunta a la IA", + "EEvZiHhB": "Explorar ideas sobre ", + "FGTvbaty": "¿Te gustaría publicar este resumen en el hilo original? También puedes pedir a Copilot que haga cambios.", + "HMUo+5uG": "Habilitar visión", + "HOkdCgNn": "Limite de tokens", + "HYbZtR6A": "Habilita las trazas de las peticiones LLM. Muestra la conversación entera en los logs.", + "HberkbyG": "Reacciona por mi", + "HigwY2IC": "Restricciones de usuarios (experimental)", + "Hn93Hjdt": "Esta extensión esta actualmente en beta. Para reportar errores o enviar sugerencias crea un nuevo reporte en el repositorio de la extensión.", + "JCIgkjKX": "Nombre de usuario", + "JLL4ie2j": "Acciones de IA", + "Ku669Gj+": "Agenda de reunión", + "LeYMnIU1": "Publicar resumen", + "LskuXn8V": "Permitir Canales Privados:", + "MntrZeJt": "Cargar Imagen", + "N1MjLfHK": "Permitir IDs de equipo (csv):", + "Ncjgeg3G": "Escribe la agenda para una reunión sobre ", + "OyOTNe+S": "Avatar del bot", + "S24j7sXB": "Escribe una lista de pros y cons sobre ", + "S9zhSWmI": "Falta información", + "TpaMMxR8": "Los miembros del equipo pueden mencionar este bot con este nombre de usuario", + "UvNpDP3m": "Pros y Cons", + "V4TGblFD": "Nombre de usuario del bot", + "VRUze7ht": "¿Como te gustaría que la IA respondiera?", + "VfxfZ8Lf": "Habilitar restricciones para permitir o no a los usuarios usar la IA en esta instancia.", + "XK7rtqla": "Escribe una lista de tareas sobre ", + "XaCdJb86": "Resume el hilo", + "YGyAkqd5": "Generar Con:", + "YmXaPq7f": "Los servicios de IA son servicios de terceros; Mattermost no es responsable de los mensajes generados.", + "Z17cukDt": "Historial de conversaciones", + "aH3xyeJP": "Elije que bot quieres que se use por defecto para cada función.", + "bV+YmcFC": "Modelo por defecto", + "cTgKF+6f": "Solo usuarios en el equipo:", + "cZ+mfu9J": "falso", + "dOQCL8n7": "Nombre a mostrar", + "eMUupPIl": "Ponerte día rápidamente con resúmenes instantáneos de canales e hilos.", + "eQUYygRa": "Lista de tareas", + "eiVgJmO6": "Añadir un bot de IA", + "faKga4wz": "Limite de tiempo de streaming en segundos", + "gY19rcnT": "Encontrar preguntas abiertas", + "i04PqEZU": "Copilot no ha sido configurado todavia en este espacio de trabajo", + "jWHIuwto": "Ver historial de conversación", + "kMoYLtG8": "Copilot esta aquí para ayudar. Elige de entre las preguntas abajo o escribe la tuya propia.", + "kSDNX67w": "verdadero", + "l4dlHzot": "Copilot es una extensión que te habilita para sacar partido del poder de la IA para:", + "pvmoJR47": "¿Qué es Copilot?", + "lOgYVyAe": "URL de la API", + "n7yYXG7R": "Servicio", + "oLNF8HT5": "Bots IA", + "sW9GShHD": "Configuración global para todas las configuraciones debajo.", + "uLBt7sJr": "Explorar ideas", + "uklLqD3r": "Usa múltiple bots de IA en el plan Enterprise", + "vroSRZd5": "BETA", + "yOs8epTG": "Se pueden configurar múltiples servicios de IA abajo.", + "z3UjXRZw": "Depurar", + "zrQ5LJLt": "Crear un resumen de una reunión en un instante." +} diff --git a/webapp/src/index.tsx b/webapp/src/index.tsx index 79120f1e..b54908a8 100644 --- a/webapp/src/index.tsx +++ b/webapp/src/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {Store, Action} from 'redux'; import styled from 'styled-components'; +import {FormattedMessage} from 'react-intl'; import {GlobalState} from '@mattermost/types/lib/store'; @@ -74,6 +75,15 @@ export default class Plugin { public async initialize(registry: any, store: WebappStore) { setupRedux(registry, store); + registry.registerTranslations((locale: string) => { + try { + // eslint-disable-next-line global-require + return require(`./i18n/${locale}.json`); + } catch (e) { + return {}; + } + }); + let rhs: any = null; if ((window as any).Components.CreatePost) { rhs = registry.registerRightHandSidebarComponent(RHS, RHSTitle); @@ -135,7 +145,7 @@ export default class Plugin { if (registry.registerPostActionComponent) { registry.registerPostActionComponent(PostMenu); } else { - registry.registerPostDropdownMenuAction(<>{'Summarize Thread'}, (postId: string) => { + registry.registerPostDropdownMenuAction(<>, (postId: string) => { const state = store.getState(); const team = state.entities.teams.teams[state.entities.teams.currentTeamId]; window.WebappUtils.browserHistory.push('/' + team.name + '/messages/@' + BotUsername); @@ -144,7 +154,7 @@ export default class Plugin { store.dispatch(rhs.showRHSPlugin); } }); - registry.registerPostDropdownMenuAction(<>{'React for me'}, doReaction); + registry.registerPostDropdownMenuAction(<>, doReaction); } registry.registerAdminConsoleCustomSetting('Config', Config); diff --git a/webapp/webpack.config.js b/webapp/webpack.config.js index 35db8b58..402d406c 100644 --- a/webapp/webpack.config.js +++ b/webapp/webpack.config.js @@ -66,6 +66,10 @@ const config = { }, }, }, + { + test: /\.json$/, + type: 'json', + }, { test: /\.(png|eot|tiff|svg|woff2|woff|ttf|gif|mp3|jpg|jpeg)$/, type: 'asset/inline', @@ -78,6 +82,7 @@ const config = { redux: 'Redux', 'react-redux': 'ReactRedux', 'prop-types': 'PropTypes', + 'react-intl': 'ReactIntl', 'react-bootstrap': 'ReactBootstrap', 'react-router-dom': 'ReactRouterDom', },