diff --git a/.tx/config b/.tx/config new file mode 100644 index 0000000..1b4af26 --- /dev/null +++ b/.tx/config @@ -0,0 +1,8 @@ +[main] +host = https://www.transifex.com + +[o:open-edx:p:edx-platform:r:frontend-component-ai-translations] +file_filter = src/i18n/messages/.json +source_file = src/i18n/transifex_input.json +source_lang = en +type = KEYVALUEJSON diff --git a/Makefile b/Makefile index f4913e2..df00363 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -export TRANSIFEX_RESOURCE = frontend-component-header-edx -transifex_resource = frontend-component-header-edx +export TRANSIFEX_RESOURCE = frontend-component-ai-translations +transifex_resource = frontend-component-ai-translations transifex_langs = "ar,fr,es_419,zh_CN,pt,it,de,uk,ru,hi,fr_CA" transifex_utils = ./node_modules/.bin/transifex-utils.js diff --git a/package-lock.json b/package-lock.json index ce35b72..e668d73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,9 @@ "@testing-library/react": "10.4.9", "@testing-library/user-event": "^14.5.1", "@wojtekmaj/enzyme-adapter-react-17": "0.8.0", + "babel-plugin-react-intl": "^8.2.25", "enzyme": "3.11.0", + "glob": "7.2.0", "husky": "8.0.3", "jest": "29.7.0" }, @@ -2210,6 +2212,24 @@ "react": "^16.9.0 || ^17.0.0" } }, + "node_modules/@edx/frontend-build/node_modules/@formatjs/ts-transformer": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz", + "integrity": "sha512-mu7sHXZk1NWZrQ3eUqugpSYo8x5/tXkrI4uIbFqCEC0eNgQaIcoKgVeDFgDAcgG+cEme2atAUYSFF+DFWC4org==", + "dependencies": { + "intl-messageformat-parser": "6.1.2", + "tslib": "^2.0.1", + "typescript": "^4.0" + }, + "peerDependencies": { + "ts-jest": "^26.4.0" + }, + "peerDependenciesMeta": { + "ts-jest": { + "optional": true + } + } + }, "node_modules/@edx/frontend-build/node_modules/@jest/console": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", @@ -2402,6 +2422,33 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@edx/frontend-build/node_modules/babel-plugin-react-intl": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-7.9.4.tgz", + "integrity": "sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==", + "deprecated": "this package has been renamed to babel-plugin-formatjs", + "dependencies": { + "@babel/core": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/types": "^7.9.5", + "@formatjs/ts-transformer": "^2.6.0", + "@types/babel__core": "^7.1.7", + "@types/fs-extra": "^9.0.1", + "@types/schema-utils": "^2.4.0", + "fs-extra": "^9.0.0", + "intl-messageformat-parser": "^5.3.7", + "schema-utils": "^2.6.6" + } + }, + "node_modules/@edx/frontend-build/node_modules/babel-plugin-react-intl/node_modules/intl-messageformat-parser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz", + "integrity": "sha512-TvB3LqF2VtP6yI6HXlRT5TxX98HKha6hCcrg9dwlPwNaedVNuQA9KgBdtWKgiyakyCTYHQ+KJeFEstNKfZr64w==", + "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser", + "dependencies": { + "@formatjs/intl-numberformat": "^5.5.2" + } + }, "node_modules/@edx/frontend-build/node_modules/cjs-module-lexer": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", @@ -2981,6 +3028,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@edx/frontend-build/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/@edx/frontend-build/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -2997,6 +3061,18 @@ "node": ">=0.10.0" } }, + "node_modules/@edx/frontend-build/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@edx/frontend-build/node_modules/v8-to-istanbul": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", @@ -3178,6 +3254,25 @@ "tslib": "^2.1.0" } }, + "node_modules/@edx/frontend-platform/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@edx/frontend-platform/node_modules/intl-messageformat": { "version": "9.13.0", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", @@ -6705,28 +6800,20 @@ } }, "node_modules/babel-plugin-react-intl": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-7.9.4.tgz", - "integrity": "sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==", + "version": "8.2.25", + "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-8.2.25.tgz", + "integrity": "sha512-vqzRwqxMKHBKEpzWIIabxUXSBYd8urOkk49nQdzgEt55tLIuDc1XdHceeMNaeJt9VRLYZUiL5vpYpnvrntUNMQ==", "deprecated": "this package has been renamed to babel-plugin-formatjs", + "dev": true, "dependencies": { "@babel/core": "^7.9.0", "@babel/helper-plugin-utils": "^7.8.3", "@babel/types": "^7.9.5", - "@formatjs/ts-transformer": "^2.6.0", + "@formatjs/ts-transformer": "2.13.0", "@types/babel__core": "^7.1.7", - "@types/fs-extra": "^9.0.1", "@types/schema-utils": "^2.4.0", - "fs-extra": "^9.0.0", - "intl-messageformat-parser": "^5.3.7", - "schema-utils": "^2.6.6" - } - }, - "node_modules/babel-plugin-react-intl/node_modules/@formatjs/ecma402-abstract": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz", - "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==", - "dependencies": { + "intl-messageformat-parser": "6.1.2", + "schema-utils": "^3.0.0", "tslib": "^2.0.1" } }, @@ -6734,6 +6821,7 @@ "version": "2.13.0", "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz", "integrity": "sha512-mu7sHXZk1NWZrQ3eUqugpSYo8x5/tXkrI4uIbFqCEC0eNgQaIcoKgVeDFgDAcgG+cEme2atAUYSFF+DFWC4org==", + "dev": true, "dependencies": { "intl-messageformat-parser": "6.1.2", "tslib": "^2.0.1", @@ -6748,37 +6836,11 @@ } } }, - "node_modules/babel-plugin-react-intl/node_modules/@formatjs/ts-transformer/node_modules/intl-messageformat-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.1.2.tgz", - "integrity": "sha512-4GQDEPhl/ZMNDKwMsLqyw1LG2IAWjmLJXdmnRcHKeLQzpgtNYZI6lVw1279pqIkRk2MfKb9aDsVFzm565azK5A==", - "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser", - "dependencies": { - "@formatjs/ecma402-abstract": "1.5.0", - "tslib": "^2.0.1" - } - }, - "node_modules/babel-plugin-react-intl/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/babel-plugin-react-intl/node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10449,14 +10511,14 @@ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", + "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -11430,12 +11492,21 @@ } }, "node_modules/intl-messageformat-parser": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz", - "integrity": "sha512-TvB3LqF2VtP6yI6HXlRT5TxX98HKha6hCcrg9dwlPwNaedVNuQA9KgBdtWKgiyakyCTYHQ+KJeFEstNKfZr64w==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.1.2.tgz", + "integrity": "sha512-4GQDEPhl/ZMNDKwMsLqyw1LG2IAWjmLJXdmnRcHKeLQzpgtNYZI6lVw1279pqIkRk2MfKb9aDsVFzm565azK5A==", "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser", "dependencies": { - "@formatjs/intl-numberformat": "^5.5.2" + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } + }, + "node_modules/intl-messageformat-parser/node_modules/@formatjs/ecma402-abstract": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz", + "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==", + "dependencies": { + "tslib": "^2.0.1" } }, "node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract": { @@ -23433,6 +23504,16 @@ "webpack-merge": "5.9.0" }, "dependencies": { + "@formatjs/ts-transformer": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz", + "integrity": "sha512-mu7sHXZk1NWZrQ3eUqugpSYo8x5/tXkrI4uIbFqCEC0eNgQaIcoKgVeDFgDAcgG+cEme2atAUYSFF+DFWC4org==", + "requires": { + "intl-messageformat-parser": "6.1.2", + "tslib": "^2.0.1", + "typescript": "^4.0" + } + }, "@jest/console": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", @@ -23596,6 +23677,33 @@ "@sinonjs/commons": "^1.7.0" } }, + "babel-plugin-react-intl": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-7.9.4.tgz", + "integrity": "sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==", + "requires": { + "@babel/core": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/types": "^7.9.5", + "@formatjs/ts-transformer": "^2.6.0", + "@types/babel__core": "^7.1.7", + "@types/fs-extra": "^9.0.1", + "@types/schema-utils": "^2.4.0", + "fs-extra": "^9.0.0", + "intl-messageformat-parser": "^5.3.7", + "schema-utils": "^2.6.6" + }, + "dependencies": { + "intl-messageformat-parser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz", + "integrity": "sha512-TvB3LqF2VtP6yI6HXlRT5TxX98HKha6hCcrg9dwlPwNaedVNuQA9KgBdtWKgiyakyCTYHQ+KJeFEstNKfZr64w==", + "requires": { + "@formatjs/intl-numberformat": "^5.5.2" + } + } + } + }, "cjs-module-lexer": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", @@ -24037,6 +24145,16 @@ "glob": "^7.1.3" } }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -24047,6 +24165,11 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + }, "v8-to-istanbul": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", @@ -24197,6 +24320,19 @@ "tslib": "^2.1.0" } }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "intl-messageformat": { "version": "9.13.0", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", @@ -26974,65 +27110,38 @@ } }, "babel-plugin-react-intl": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-7.9.4.tgz", - "integrity": "sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==", + "version": "8.2.25", + "resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-8.2.25.tgz", + "integrity": "sha512-vqzRwqxMKHBKEpzWIIabxUXSBYd8urOkk49nQdzgEt55tLIuDc1XdHceeMNaeJt9VRLYZUiL5vpYpnvrntUNMQ==", + "dev": true, "requires": { "@babel/core": "^7.9.0", "@babel/helper-plugin-utils": "^7.8.3", "@babel/types": "^7.9.5", - "@formatjs/ts-transformer": "^2.6.0", + "@formatjs/ts-transformer": "2.13.0", "@types/babel__core": "^7.1.7", - "@types/fs-extra": "^9.0.1", "@types/schema-utils": "^2.4.0", - "fs-extra": "^9.0.0", - "intl-messageformat-parser": "^5.3.7", - "schema-utils": "^2.6.6" + "intl-messageformat-parser": "6.1.2", + "schema-utils": "^3.0.0", + "tslib": "^2.0.1" }, "dependencies": { - "@formatjs/ecma402-abstract": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz", - "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==", - "requires": { - "tslib": "^2.0.1" - } - }, "@formatjs/ts-transformer": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz", "integrity": "sha512-mu7sHXZk1NWZrQ3eUqugpSYo8x5/tXkrI4uIbFqCEC0eNgQaIcoKgVeDFgDAcgG+cEme2atAUYSFF+DFWC4org==", + "dev": true, "requires": { "intl-messageformat-parser": "6.1.2", "tslib": "^2.0.1", "typescript": "^4.0" - }, - "dependencies": { - "intl-messageformat-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.1.2.tgz", - "integrity": "sha512-4GQDEPhl/ZMNDKwMsLqyw1LG2IAWjmLJXdmnRcHKeLQzpgtNYZI6lVw1279pqIkRk2MfKb9aDsVFzm565azK5A==", - "requires": { - "@formatjs/ecma402-abstract": "1.5.0", - "tslib": "^2.0.1" - } - } - } - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" } }, "typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true } } }, @@ -29770,14 +29879,14 @@ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", + "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -30498,11 +30607,22 @@ } }, "intl-messageformat-parser": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz", - "integrity": "sha512-TvB3LqF2VtP6yI6HXlRT5TxX98HKha6hCcrg9dwlPwNaedVNuQA9KgBdtWKgiyakyCTYHQ+KJeFEstNKfZr64w==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.1.2.tgz", + "integrity": "sha512-4GQDEPhl/ZMNDKwMsLqyw1LG2IAWjmLJXdmnRcHKeLQzpgtNYZI6lVw1279pqIkRk2MfKb9aDsVFzm565azK5A==", "requires": { - "@formatjs/intl-numberformat": "^5.5.2" + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz", + "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==", + "requires": { + "tslib": "^2.0.1" + } + } } }, "invariant": { diff --git a/package.json b/package.json index fa9d4e1..b462085 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "scripts": { "build": "make build", - "i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null", + "i18n_extract": "fedx-scripts formatjs extract", "lint": "fedx-scripts eslint --ext .js --ext .jsx .", "snapshot": "fedx-scripts jest --updateSnapshot", "start": "fedx-scripts webpack-dev-server --progress", @@ -39,7 +39,9 @@ "@testing-library/react": "10.4.9", "@testing-library/user-event": "^14.5.1", "@wojtekmaj/enzyme-adapter-react-17": "0.8.0", + "babel-plugin-react-intl": "^8.2.25", "enzyme": "3.11.0", + "glob": "7.2.0", "husky": "8.0.3", "jest": "29.7.0" }, diff --git a/src/App.jsx b/src/App.jsx index 036b775..fb0568e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,12 @@ import { useState } from 'react'; +import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; import { ActionRow, Collapsible, Icon, IconButton, Image, } from '@edx/paragon'; import { ChevronLeft, ChevronRight, Close } from '@edx/paragon/icons'; import PropTypes from 'prop-types'; +import messages from './messages'; import XpertLogo from './XpertLogo'; const App = ({ setIsAiTranslations, closeTranscriptSettings }) => { @@ -14,6 +16,7 @@ const App = ({ setIsAiTranslations, closeTranscriptSettings }) => { translationsError: false, view: '', }); + const intl = useIntl(); const handleAppState = (updatedData) => { setAppState((previousState) => ({ ...previousState, ...updatedData })); @@ -45,7 +48,9 @@ const App = ({ setIsAiTranslations, closeTranscriptSettings }) => { >
- Get free translations +

+ +

{ size="sm" iconAs={Icon} src={ChevronLeft} - alt="back button to main transcript settings view" + alt={intl.formatMessage(messages.backButtonAlt)} onClick={handleBackButton} /> @@ -82,12 +87,12 @@ const App = ({ setIsAiTranslations, closeTranscriptSettings }) => { setIsAiTranslations(false); }} src={Close} - alt="close settings" + alt={intl.formatMessage(messages.closeButtonAlt)} data-testid="action-row-close-btn" />
- Translations is not available +
)} diff --git a/src/App.test.jsx b/src/App.test.jsx index a40d160..ee256b4 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -1,9 +1,11 @@ import * as React from 'react'; import { act, render, screen } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; // eslint-disable-next-line import/no-extraneous-dependencies import userEvent from '@testing-library/user-event'; import App from './App'; +import messages from './messages'; const courseId = 'Fake-ID'; @@ -11,15 +13,17 @@ describe('App', () => { it('renders collapsible button', () => { const onSetIsAiTranslations = jest.fn(); render( - {}} - courseId={courseId} - />, + + {}} + courseId={courseId} + /> + , ); expect( - screen.getByText(/Get free translations/), + screen.getByText(messages.getFreeTranslations.defaultMessage), ).toBeInTheDocument(); }); @@ -27,10 +31,12 @@ describe('App', () => { const onSetIsAiTranslations = jest.fn(); render( - {}} - />, + + {}} + /> + , ); userEvent.click(screen.getByTestId('app-entry-btn')); @@ -40,16 +46,18 @@ describe('App', () => { it('goes back to previous view', async () => { const onSetIsAiTranslations = jest.fn(); render( - {}} - />, + + {}} + /> + , ); userEvent.click( - screen.getByText(/Get free translations/), + screen.getByText(messages.getFreeTranslations.defaultMessage), ); - expect(await screen.findByText(/Translations is not available/)).toBeInTheDocument(); + expect(await screen.findByText(messages.translationsNotAvailable.defaultMessage)).toBeInTheDocument(); expect(await screen.findByTestId('action-row-back-btn')).toBeInTheDocument(); await act(async () => { @@ -57,7 +65,7 @@ describe('App', () => { }); expect(onSetIsAiTranslations).toHaveBeenCalled(); - expect(await screen.findByText(/Get free translations/)).toBeInTheDocument(); + expect(await screen.findByText(messages.getFreeTranslations.defaultMessage)).toBeInTheDocument(); expect(screen.queryByText(/Get free translations is not available/)).not.toBeInTheDocument(); }); @@ -65,16 +73,18 @@ describe('App', () => { const onSetIsAiTranslations = jest.fn(); const onCloseTranscriptSettings = jest.fn(); render( - , + + + , ); userEvent.click( - screen.getByText(/Get free translations/), + screen.getByText(messages.getFreeTranslations.defaultMessage), ); - expect(await screen.findByText(/Translations is not available/)).toBeInTheDocument(); + expect(await screen.findByText(messages.translationsNotAvailable.defaultMessage)).toBeInTheDocument(); expect(await screen.findByTestId('action-row-close-btn')).toBeInTheDocument(); await userEvent.click(screen.queryByTestId('action-row-close-btn')); diff --git a/src/i18n/index.js b/src/i18n/index.js new file mode 100644 index 0000000..2c9957b --- /dev/null +++ b/src/i18n/index.js @@ -0,0 +1,38 @@ +import { messages as paragonMessages } from '@edx/paragon'; +import arMessages from './messages/ar.json'; +import frMessages from './messages/fr.json'; +import es419Messages from './messages/es_419.json'; +import zhcnMessages from './messages/zh_CN.json'; +import ptMessages from './messages/pt.json'; +import itMessages from './messages/it.json'; +import ukMessages from './messages/uk.json'; +import deMessages from './messages/de.json'; +import ruMessages from './messages/ru.json'; +import hiMessages from './messages/hi.json'; +import frCAMessages from './messages/fr_CA.json'; +import dedeMessages from './messages/de_DE.json'; +import ititMessages from './messages/it_IT.json'; +import ptptMessages from './messages/pt_PT.json'; +// no need to import en messages-- they are in the defaultMessage field + +const appMessages = { + ar: arMessages, + 'es-419': es419Messages, + fr: frMessages, + 'zh-cn': zhcnMessages, + pt: ptMessages, + it: itMessages, + de: deMessages, + hi: hiMessages, + 'fr-ca': frCAMessages, + ru: ruMessages, + uk: ukMessages, + 'de-de': dedeMessages, + 'it-it': ititMessages, + 'pt-pt': ptptMessages, +}; + +export default [ + paragonMessages, + appMessages, +]; diff --git a/src/i18n/index.test.js b/src/i18n/index.test.js new file mode 100644 index 0000000..a03bddc --- /dev/null +++ b/src/i18n/index.test.js @@ -0,0 +1,23 @@ +import messages from './index'; + +describe('messages', () => { + it('contains app messages', () => { + expect(messages.length).toEqual(2); + const appMessages = messages[1]; + const languages = Object.getOwnPropertyNames(appMessages); + expect(languages).toContainEqual('ar'); + expect(languages).toContainEqual('es-419'); + expect(languages).toContainEqual('fr'); + expect(languages).toContainEqual('zh-cn'); + expect(languages).toContainEqual('pt'); + expect(languages).toContainEqual('it'); + expect(languages).toContainEqual('de'); + expect(languages).toContainEqual('hi'); + expect(languages).toContainEqual('fr-ca'); + expect(languages).toContainEqual('ru'); + expect(languages).toContainEqual('uk'); + expect(languages).toContainEqual('de-de'); + expect(languages).toContainEqual('it-it'); + expect(languages).toContainEqual('pt-pt'); + }); +}); diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/ar.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/de.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/de_DE.json b/src/i18n/messages/de_DE.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/de_DE.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/es_419.json b/src/i18n/messages/es_419.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/es_419.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/fa_IR.json b/src/i18n/messages/fa_IR.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/fa_IR.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/fr.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/fr_CA.json b/src/i18n/messages/fr_CA.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/fr_CA.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/hi.json b/src/i18n/messages/hi.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/hi.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/it.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/it_IT.json b/src/i18n/messages/it_IT.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/it_IT.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/pt.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/pt_PT.json b/src/i18n/messages/pt_PT.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/pt_PT.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/ru.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/uk.json b/src/i18n/messages/uk.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/uk.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/messages/zh_CN.json b/src/i18n/messages/zh_CN.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/messages/zh_CN.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/messages.js b/src/messages.js new file mode 100644 index 0000000..957d321 --- /dev/null +++ b/src/messages.js @@ -0,0 +1,26 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + getFreeTranslations: { + id: 'ai-translations.request.heading.top', + defaultMessage: 'Get free translations', + description: 'Translations heading available', + }, + translationsNotAvailable: { + id: 'ai-translations.request.heading.bottom', + defaultMessage: 'Translations service is not available', + description: 'Translations heading not available', + }, + backButtonAlt: { + id: 'ai-translations.icon.button.back.alt', + defaultMessage: 'Back button to main transcript settings view', + description: 'alt text for back icon button', + }, + closeButtonAlt: { + id: 'ai-translations.icon.button.close.alt', + defaultMessage: 'Close settings', + description: 'alt text for close settings icon button', + }, +}); + +export default messages;