diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..b220662565 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,153 @@ +{ + "name": "marquez", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@types/react": "^18.0.25", + "i18next": "^22.0.6", + "i18next-browser-languagedetector": "^7.0.1", + "react-i18next": "^12.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "dependencies": { + "regenerator-runtime": "^0.13.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/react": { + "version": "18.0.25", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz", + "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/i18next": { + "version": "22.0.6", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.0.6.tgz", + "integrity": "sha512-RlreNGoPIdDP4QG+qSA9PxZKGwlzmcozbI9ObI6+OyUa/Rp0EjZZA9ubyBjw887zVNZsC+7FI3sXX8oiTzAfig==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.17.2" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz", + "integrity": "sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==", + "dependencies": { + "@babel/runtime": "^7.19.4" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-i18next": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.0.0.tgz", + "integrity": "sha512-/O7N6aIEAl1FaWZBNvhdIo9itvF/MO/nRKr9pYqRc9LhuC1u21SlfwpiYQqvaeNSEW3g3qUXLREOWMt+gxrWbg==", + "dependencies": { + "@babel/runtime": "^7.14.5", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 19.0.0", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000..625e319055 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "@types/react": "^18.0.25", + "i18next": "^22.0.6", + "i18next-browser-languagedetector": "^7.0.1", + "react-i18next": "^12.0.0" + } +} diff --git a/web/src/components/header/Header.tsx b/web/src/components/header/Header.tsx index cf08512f7c..8ebe259766 100644 --- a/web/src/components/header/Header.tsx +++ b/web/src/components/header/Header.tsx @@ -10,6 +10,7 @@ import MqText from '../core/text/MqText' import NamespaceSelect from '../namespace-select/NamespaceSelect' import React, { ReactElement } from 'react' import Search from '../search/Search' +import { useTranslation } from 'react-i18next'; const styles = (theme: Theme) => { return createStyles({ @@ -40,6 +41,7 @@ const styles = (theme: Theme) => { type HeaderProps = WithStyles const Header = (props: HeaderProps): ReactElement => { + const { t } = useTranslation(); const { classes } = props return ( @@ -53,7 +55,8 @@ const Header = (props: HeaderProps): ReactElement => { - API Docs + {/* API Docs */} + {t('header')} diff --git a/web/src/components/jobs/JobDetailPage.tsx b/web/src/components/jobs/JobDetailPage.tsx index 101a60c15a..5b1d59b3cc 100644 --- a/web/src/components/jobs/JobDetailPage.tsx +++ b/web/src/components/jobs/JobDetailPage.tsx @@ -24,6 +24,7 @@ import MqText from '../core/text/MqText' import RunInfo from './RunInfo' import RunStatus from './RunStatus' import Runs from './Runs' +import { useTranslation } from 'react-i18next'; const styles = ({ spacing }: ITheme) => { return createStyles({ @@ -52,6 +53,7 @@ const JobDetailPage: FunctionComponent = props => { const handleChange = (event: ChangeEvent, newValue: SetStateAction) => { setTab(newValue) } + const { t } = useTranslation(); useEffect(() => { fetchRuns(job.name, job.namespace) @@ -82,8 +84,8 @@ const JobDetailPage: FunctionComponent = props => { > - - + + diff --git a/web/src/i18n.js b/web/src/i18n.js new file mode 100644 index 0000000000..f7973c98be --- /dev/null +++ b/web/src/i18n.js @@ -0,0 +1,46 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; + +i18n + // detect user language + // learn more: https://github.com/i18next/i18next-browser-languageDetector + .use(LanguageDetector) + // pass the i18n instance to react-i18next. + .use(initReactI18next) + // init i18next + // for all options read: https://www.i18next.com/overview/configuration-options + .init({ + debug: true, + fallbackLng: 'en', + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + resources: { + en: { + translation: { + // here we will place our translations... + header: 'API Docs', + JobTab1: 'LATEST RUN', + JobTab2: 'RUN HISTORY' + } + }, + fr: { + translation: { + header: 'API Docs', + JobTab1: 'DERNIÈRE COURSE', + JobTab2: "HISTORIQUE D'EXECUTION" + } + }, + sp: { + translation: { + header: 'API Docs', + JobTab1: 'ÚLTIMA EJECUCIÓN', + JobTab2: 'HISTORIAL DE EJECUCIONES' + } + } + + } + }); + +export default i18n; \ No newline at end of file diff --git a/web/src/index.tsx b/web/src/index.tsx index cc39edd0ee..a109f69686 100644 --- a/web/src/index.tsx +++ b/web/src/index.tsx @@ -5,4 +5,7 @@ import App from './components/App' // fonts import './index.css' +// i18n +import './i18n.js'; + ReactDOM.render(, document.getElementById('root'))