diff --git a/package.json b/package.json index d4b45c7..71bd26d 100644 --- a/package.json +++ b/package.json @@ -37,14 +37,15 @@ "devDependencies": { "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", + "@formatjs/ts-transformer": "^3.13.20", "@mui/icons-material": "^5.2.5", "@mui/material": "^5.2.5", "@types/express": "^4.17.11", "@types/lodash": "^4.14.168", "@types/morgan": "^1.9.2", "@types/node": "^14.14.41", - "@types/react": "^17.0.3", - "@types/react-dom": "^17.0.3", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "@types/yup": "^0.29.11", "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", @@ -56,8 +57,9 @@ "nodemon": "^3.0.1", "postcss": "^8.2.10", "prettier": "^2.2.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-intl": "^6.8.4", "rimraf": "^6.0.1", "swr": "^2.2.4", "terser-webpack-plugin": "^5.3.9", diff --git a/src/frontend/components/App.tsx b/src/frontend/components/App.tsx index 2a32d0d..de74fa4 100644 --- a/src/frontend/components/App.tsx +++ b/src/frontend/components/App.tsx @@ -1,10 +1,12 @@ import CssBaseline from '@mui/material/CssBaseline'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import React, { FunctionComponent, useEffect, useState } from 'react'; +import { IntlProvider } from 'react-intl'; import { mutate } from 'swr'; import { deleteEntry, patchEntry } from '../api'; import { useAllEntries, usePreferDarkMode } from '../hooks'; +import { getBrowserLanguage, translations } from '../i18n'; import { EntryType, SavedEntry } from '../types'; import { Content } from './Content'; @@ -32,6 +34,7 @@ export const App: FunctionComponent = () => { const theme = createTheme({ palette: { mode: preferDarkMode ? 'dark' : 'light' }, }); + const language = getBrowserLanguage(); const handleTabChange = (selectedTab: EntryType) => { setState((oldState) => ({ @@ -127,35 +130,37 @@ export const App: FunctionComponent = () => { return ( - - - - - - + + + + + + + + ); }; diff --git a/src/frontend/components/DeleteAllEntriesListItem.tsx b/src/frontend/components/DeleteAllEntriesListItem.tsx index aa09e0c..986ea30 100644 --- a/src/frontend/components/DeleteAllEntriesListItem.tsx +++ b/src/frontend/components/DeleteAllEntriesListItem.tsx @@ -1,9 +1,10 @@ import IconButton from '@mui/material/IconButton'; -import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import DeleteIcon from '@mui/icons-material/Delete'; import React, { FunctionComponent, useState } from 'react'; +import { FormattedMessage } from 'react-intl'; import { DeleteAllConfirmationDialog } from './dialog'; @@ -32,14 +33,18 @@ export const DeleteAllEntriesListItem: FunctionComponent - + - - + + } + /> + = ({ }; return ( - + = ({ - + ); }; diff --git a/src/frontend/components/Toolbar.tsx b/src/frontend/components/Toolbar.tsx index fc2d575..6b1849f 100644 --- a/src/frontend/components/Toolbar.tsx +++ b/src/frontend/components/Toolbar.tsx @@ -7,7 +7,7 @@ import Typography from '@mui/material/Typography'; import AddIcon from '@mui/icons-material/Add'; import CheckBoxOutlineBlankOutlinedIcon from '@mui/icons-material/CheckBoxOutlineBlankOutlined'; import CheckBoxOutlinedIcon from '@mui/icons-material/CheckBoxOutlined'; -import React, { ChangeEvent, FunctionComponent } from 'react'; +import React, { FunctionComponent, SyntheticEvent } from 'react'; import { EntryType } from '../types'; @@ -24,7 +24,7 @@ export const Toolbar: FunctionComponent = ({ onTabChange, selectedTab, }) => { - const handleTabChange = (ev: ChangeEvent, selectedTab: EntryType) => + const handleTabChange = (ev: SyntheticEvent, selectedTab: EntryType) => onTabChange(selectedTab); return ( diff --git a/src/frontend/components/dialog/AddEntryDialog.tsx b/src/frontend/components/dialog/AddEntryDialog.tsx index ce21023..a274601 100644 --- a/src/frontend/components/dialog/AddEntryDialog.tsx +++ b/src/frontend/components/dialog/AddEntryDialog.tsx @@ -1,4 +1,5 @@ import React, { FunctionComponent } from 'react'; +import { FormattedMessage } from 'react-intl'; import { createEntry } from '../../api'; @@ -21,7 +22,9 @@ export const AddEntryDialog: FunctionComponent = ({ onClose={onClose} onSubmit={handleSubmit} open={open} - title="Add new entry" + title={ + + } /> ); }; diff --git a/src/frontend/components/dialog/DeleteAllConfirmationDialog.tsx b/src/frontend/components/dialog/DeleteAllConfirmationDialog.tsx index 608f86b..b84b0ff 100644 --- a/src/frontend/components/dialog/DeleteAllConfirmationDialog.tsx +++ b/src/frontend/components/dialog/DeleteAllConfirmationDialog.tsx @@ -3,6 +3,7 @@ import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import React, { FunctionComponent } from 'react'; +import { FormattedMessage } from 'react-intl'; export type DeleteAllConfirmationDialogProps = { onAnswer: (answer: boolean) => void; @@ -18,14 +19,17 @@ export const DeleteAllConfirmationDialog: FunctionComponent - Do you really want to delete all entries marked as done? + diff --git a/src/frontend/components/dialog/EditEntryDialog.tsx b/src/frontend/components/dialog/EditEntryDialog.tsx index b0a411b..87f495c 100644 --- a/src/frontend/components/dialog/EditEntryDialog.tsx +++ b/src/frontend/components/dialog/EditEntryDialog.tsx @@ -4,6 +4,7 @@ import { patchEntry } from '../../api'; import { SavedEntry } from '../../types'; import { EntryDialogBase, EntryDialogValues } from './EntryDialogBase'; +import { FormattedMessage } from 'react-intl'; export type EditEntryDialogProps = { entry?: SavedEntry; @@ -31,7 +32,7 @@ export const EditEntryDialog: FunctionComponent = ({ onClose={onClose} onSubmit={handleSubmit} open={open} - title="Edit entry" + title={} /> ); }; diff --git a/src/frontend/components/dialog/EntryDialogBase.tsx b/src/frontend/components/dialog/EntryDialogBase.tsx index 2042b55..1c762f8 100644 --- a/src/frontend/components/dialog/EntryDialogBase.tsx +++ b/src/frontend/components/dialog/EntryDialogBase.tsx @@ -9,9 +9,11 @@ import React, { ChangeEvent, FormEvent, FunctionComponent, + ReactNode, useEffect, useState, } from 'react'; +import { FormattedMessage } from 'react-intl'; import { mutate } from 'swr'; export type EntryDialogValues = { @@ -24,7 +26,7 @@ export type EntryDialogBaseProps = { onClose: () => void; onSubmit: (values: EntryDialogValues) => Promise; open: boolean; - title: string; + title: ReactNode; }; export const EntryDialogBase: FunctionComponent = ({ @@ -77,12 +79,15 @@ export const EntryDialogBase: FunctionComponent = ({ {error && ( - Unable to save changes. + )} } fullWidth required value={text} @@ -92,7 +97,7 @@ export const EntryDialogBase: FunctionComponent = ({ /> } fullWidth value={url} onChange={handleURLChange} @@ -101,10 +106,10 @@ export const EntryDialogBase: FunctionComponent = ({ diff --git a/src/frontend/components/snackbar/ErrorSnackbar.tsx b/src/frontend/components/snackbar/ErrorSnackbar.tsx index edcc2b6..4921509 100644 --- a/src/frontend/components/snackbar/ErrorSnackbar.tsx +++ b/src/frontend/components/snackbar/ErrorSnackbar.tsx @@ -3,6 +3,7 @@ import IconButton from '@mui/material/IconButton'; import Snackbar from '@mui/material/Snackbar'; import CloseIcon from '@mui/icons-material/Close'; import React, { FunctionComponent, SyntheticEvent } from 'react'; +import { FormattedMessage } from 'react-intl'; export type ErrorSnackbarProps = { onClose: () => void; @@ -36,7 +37,12 @@ export const ErrorSnackbar: FunctionComponent = ({ autoHideDuration={6000} onClose={handleClose} > - API returned erroneous response. + + + ); }; diff --git a/src/frontend/i18n/en.json b/src/frontend/i18n/en.json new file mode 100644 index 0000000..bb8893c --- /dev/null +++ b/src/frontend/i18n/en.json @@ -0,0 +1,14 @@ +{ + "add": "Add", + "addNewEntry": "Add new entry", + "apiError": "API returned erroneous response.", + "cancel": "Cancel", + "deleteAll": "Delete all", + "deleteAllConfirmation": "Do you really want to delete all entries marked as done?", + "editEntry": "Edit entry", + "no": "No", + "text": "Text", + "url": "URL", + "unableToSaveChanges": "Unable to save changes.", + "yes": "Yes" +} diff --git a/src/frontend/i18n/fi.json b/src/frontend/i18n/fi.json new file mode 100644 index 0000000..0584986 --- /dev/null +++ b/src/frontend/i18n/fi.json @@ -0,0 +1,14 @@ +{ + "add": "Lisää", + "addNewEntry": "Lisää uusi kohta", + "apiError": "Palvelin palautti virheen.", + "cancel": "Peruuta", + "deleteAll": "Poista kaikki", + "deleteAllConfirmation": "Haluatko varmasti poistaa kaikki valmiiksi merkityt kohteet?", + "editEntry": "Muokkaa", + "no": "Ei", + "text": "Teksti", + "url": "Verkko-osoite", + "unableToSaveChanges": "Tallentaminen epäonnistui.", + "yes": "Kyllä" +} diff --git a/src/frontend/i18n/index.ts b/src/frontend/i18n/index.ts new file mode 100644 index 0000000..ff19d1c --- /dev/null +++ b/src/frontend/i18n/index.ts @@ -0,0 +1,18 @@ +import enTranslations from './en.json'; +import fiTranslations from './fi.json'; + +const SUPPORTED_LANGUAGES = new Set(['en', 'fi']); +const DEFAULT_LANGUAGE = 'en'; + +export const translations: Record> = { + en: enTranslations, + fi: fiTranslations, +}; + +export const getBrowserLanguage = (): string => { + const browserLanguageCode = navigator.language.split(/[-_]/)[0]; + + return SUPPORTED_LANGUAGES.has(browserLanguageCode) + ? browserLanguageCode + : DEFAULT_LANGUAGE; +}; diff --git a/src/frontend/index.tsx b/src/frontend/index.tsx index 776e97a..5bd8918 100644 --- a/src/frontend/index.tsx +++ b/src/frontend/index.tsx @@ -1,6 +1,9 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { App } from './components'; -ReactDOM.render(, document.getElementById('root')); +const container = document.getElementById('root'); +const root = createRoot(container!); + +root.render(); diff --git a/tsconfig.frontend.json b/tsconfig.frontend.json index c491452..dbf2f46 100644 --- a/tsconfig.frontend.json +++ b/tsconfig.frontend.json @@ -5,11 +5,18 @@ "esModuleInterop": true, "lib": ["DOM", "ESNext"], "jsx": "react", + "plugins": [ + { + "ast": true, + "import": "transform", + "overrideIdFn": "[sha512:contenthash:base64:6]", + "transform": "@formatjs/ts-transformer", + "type": "config" + } + ], + "resolveJsonModule": true, "strict": true, "target": "ES5" }, - "include": [ - "./src/common/**/*.ts", - "./src/frontend/**/*.ts", - ] + "include": ["./src/common/**/*.ts", "./src/frontend/**/*.ts"] } diff --git a/webpack.config.js b/webpack.config.js index aaab617..e15e407 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,7 @@ const path = require('path'); const TerserPlugin = require('terser-webpack-plugin'); const nodeExternals = require('webpack-node-externals'); +const { transform } = require('@formatjs/ts-transformer'); const isProduction = /^prod/.test(process.env.NODE_ENV); const mode = isProduction ? 'production' : 'development'; @@ -27,7 +28,9 @@ module.exports = [ test: /\.ts$/, exclude: /node_modules/, loader: 'ts-loader', - options: { configFile: path.resolve(__dirname, 'tsconfig.backend.json') }, + options: { + configFile: path.resolve(__dirname, 'tsconfig.backend.json'), + }, }, ], }, @@ -75,7 +78,18 @@ module.exports = [ test: /\.tsx?$/, exclude: /node_modules/, loader: 'ts-loader', - options: { configFile: path.resolve(__dirname, 'tsconfig.frontend.json') }, + options: { + configFile: path.resolve(__dirname, 'tsconfig.frontend.json'), + getCustomTransformers() { + return { + before: [ + transform({ + overrideIdFn: '[sha512:contenthash:base64:6]', + }), + ], + }; + }, + }, }, ], }, diff --git a/yarn.lock b/yarn.lock index c78bbd5..439e2de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -240,6 +240,90 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@formatjs/ecma402-abstract@2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.1.tgz#2e62bc5c22b0e6a5e13bfec6aac15d3d403e1065" + integrity sha512-O4ywpkdJybrjFc9zyL8qK5aklleIAi5O4nYhBVJaOFtCkNrnU+lKFeJOFC48zpsZQmR8Aok2V79hGpHnzbmFpg== + dependencies: + "@formatjs/fast-memoize" "2.2.2" + "@formatjs/intl-localematcher" "0.5.6" + tslib "2" + +"@formatjs/fast-memoize@2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.2.tgz#2409ec10f5f7d6c65f4c04e6c2d6cc56fa1e4cef" + integrity sha512-mzxZcS0g1pOzwZTslJOBTmLzDXseMLLvnh25ymRilCm8QLMObsQ7x/rj9GNrH0iUhZMlFisVOD6J1n6WQqpKPQ== + dependencies: + tslib "2" + +"@formatjs/icu-messageformat-parser@2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.1.tgz#f127c6a8196446c7d842c08b1230f10cf27a3dc3" + integrity sha512-7AYk4tjnLi5wBkxst2w7qFj38JLMJoqzj7BhdEl7oTlsWMlqwgx4p9oMmmvpXWTSDGNwOKBRc1SfwMh5MOHeNg== + dependencies: + "@formatjs/ecma402-abstract" "2.2.1" + "@formatjs/icu-skeleton-parser" "1.8.5" + tslib "2" + +"@formatjs/icu-skeleton-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.5.tgz#c25355778b9ea49bb0dcd85af727a375b9fcea62" + integrity sha512-zRZ/e3B5qY2+JCLs7puTzWS1Jb+t/K+8Jur/gEZpA2EjWeLDE17nsx8thyo9P48Mno7UmafnPupV2NCJXX17Dg== + dependencies: + "@formatjs/ecma402-abstract" "2.2.1" + tslib "2" + +"@formatjs/intl-displaynames@6.8.1": + version "6.8.1" + resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.8.1.tgz#9cb16ecdd08410c884f0edf9057abb23ef842a86" + integrity sha512-nyWfJk4BZ1+GzLq9a40BgVPSRpBkRAVzrSpql+92i0i+lX11m9eS1trSRf/h3j/XcQ+h1h+ntA4Ra4jETK7nNg== + dependencies: + "@formatjs/ecma402-abstract" "2.2.1" + "@formatjs/intl-localematcher" "0.5.6" + tslib "2" + +"@formatjs/intl-listformat@7.7.1": + version "7.7.1" + resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.7.1.tgz#267b7aff8ad3bf28d6608caab5dad26346a7b07c" + integrity sha512-bjBxWaUhYAbJFUlFSMWZGn3r2mglXwk+BLyGRu8dY8Q83ZPsqmmVQzjQKENHE3lV6eoQGHT2oZHxUaVndJlk6Q== + dependencies: + "@formatjs/ecma402-abstract" "2.2.1" + "@formatjs/intl-localematcher" "0.5.6" + tslib "2" + +"@formatjs/intl-localematcher@0.5.6": + version "0.5.6" + resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.6.tgz#cd0cd99483673d3196a15b4e2c924cfda7f002f8" + integrity sha512-roz1+Ba5e23AHX6KUAWmLEyTRZegM5YDuxuvkHCyK3RJddf/UXB2f+s7pOMm9ktfPGla0g+mQXOn5vsuYirnaA== + dependencies: + tslib "2" + +"@formatjs/intl@2.10.11": + version "2.10.11" + resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.10.11.tgz#f63c62cc540709b35d080188838b3dbb262f47ca" + integrity sha512-FNLZjzE1QRlv1Wf0oinnM97AbvZU1zQnQMHI0Oza2F7PxzrPf6bYFRs0ugapq/O4FrvNwDt9F9nyRNwsMM118g== + dependencies: + "@formatjs/ecma402-abstract" "2.2.1" + "@formatjs/fast-memoize" "2.2.2" + "@formatjs/icu-messageformat-parser" "2.9.1" + "@formatjs/intl-displaynames" "6.8.1" + "@formatjs/intl-listformat" "7.7.1" + intl-messageformat "10.7.3" + tslib "2" + +"@formatjs/ts-transformer@^3.13.20": + version "3.13.20" + resolved "https://registry.yarnpkg.com/@formatjs/ts-transformer/-/ts-transformer-3.13.20.tgz#f682e0649f2ec4fac7bf2db0c06f65f2891bde0b" + integrity sha512-63zGIsaLpxfu0w3jclfrgh8EBxbwO43IbCNIAWj31ZowFtgqIOBo7j/DspQCEhvpwDM2ErYfZv8DjV3cabphVQ== + dependencies: + "@formatjs/icu-messageformat-parser" "2.9.1" + "@types/json-stable-stringify" "1" + "@types/node" "14 || 16 || 17 || 18 || 20" + chalk "4" + json-stable-stringify "1" + tslib "2" + typescript "5" + "@fvilers/normalize-port@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@fvilers/normalize-port/-/normalize-port-1.0.0.tgz#411ab781f8beb5659172ff0a80d45ce860df8563" @@ -478,6 +562,14 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/hoist-non-react-statics@3": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/http-errors@*": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" @@ -488,6 +580,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/json-stable-stringify@1": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz#41393e6b7a9a67221607346af4a79783aeb28aea" + integrity sha512-ESTsHWB72QQq+pjUFIbEz9uSCZppD31YrVkbt2rnUciTYEvcwN6uZIhX5JZeBHqRlFJ41x/7MewCs7E2Qux6Cg== + "@types/lodash@^4.14.168": version "4.17.13" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb" @@ -512,6 +609,13 @@ dependencies: undici-types "~6.19.8" +"@types/node@14 || 16 || 17 || 18 || 20": + version "20.17.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.5.tgz#b7a1d8619ced7ce1da901b07a47c61107272449a" + integrity sha512-n8FYY/pRxu496441gIcAQFZPKXbhsd6VZygcq+PTSZ75eMh/Ke0hCAROdUa21qiFqKNsPPYic46yXDO1JGiPBQ== + dependencies: + undici-types "~6.19.2" + "@types/node@^14.14.41": version "14.18.63" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" @@ -537,12 +641,12 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@^17.0.3": - version "17.0.25" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5" - integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== +"@types/react-dom@^18.3.1": + version "18.3.1" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" + integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ== dependencies: - "@types/react" "^17" + "@types/react" "*" "@types/react-transition-group@^4.4.10": version "4.4.11" @@ -551,7 +655,7 @@ dependencies: "@types/react" "*" -"@types/react@*": +"@types/react@*", "@types/react@^18.3.11", "@types/react@^18.3.12": version "18.3.12" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60" integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw== @@ -559,20 +663,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@^17", "@types/react@^17.0.3": - version "17.0.83" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.83.tgz#b477c56387b74279281149dcf5ba2a1e2216d131" - integrity sha512-l0m4ArKJvmFtR4e8UmKrj1pB4tUgOhJITf+mADyF/p69Ts1YAR/E+G9XEM0mHXKVRa1dQNHseyyDNzeuAXfXQw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "^0.16" - csstype "^3.0.2" - -"@types/scheduler@^0.16": - version "0.16.8" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" - integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== - "@types/semver@^7.5.0": version "7.5.8" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" @@ -1183,6 +1273,14 @@ caniuse-lite@^1.0.30001669: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz#fe133d41fe74af8f7cc93b8a714c3e86a86e6f04" integrity sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw== +chalk@4, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1192,14 +1290,6 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chokidar@^3.5.2: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" @@ -2219,7 +2309,7 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" -hoist-non-react-statics@^3.3.1: +hoist-non-react-statics@3, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -2307,6 +2397,16 @@ interpret@^3.1.1: resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== +intl-messageformat@10.7.3: + version "10.7.3" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.7.3.tgz#a5cbc886f1db4b6324e6bfc4fd8f91a3e3e37c31" + integrity sha512-AAo/3oyh7ROfPhDuh7DxTTydh97OC+lv7h1Eq5LuHWuLsUMKOhtzTYuyXlUReuwZ9vANDHo4CS1bGRrn7TZRtg== + dependencies: + "@formatjs/ecma402-abstract" "2.2.1" + "@formatjs/fast-memoize" "2.2.2" + "@formatjs/icu-messageformat-parser" "2.9.1" + tslib "2" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -2600,6 +2700,21 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stable-stringify@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== + dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.1" + +jsonify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== + "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" @@ -3134,14 +3249,29 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -react-dom@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" + integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.2" + +react-intl@^6.8.4: + version "6.8.4" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.8.4.tgz#e39b6a71bbce5db90460abba4869c9f58b38fc1e" + integrity sha512-UKYrCIztyvSiZCpvHjDwAHlXT735fDioABVP+uhRAOhDSBS9NQ2vVbxiUikvVEBdr2b0cTe1tUfOfvhbmSPi/A== + dependencies: + "@formatjs/ecma402-abstract" "2.2.1" + "@formatjs/icu-messageformat-parser" "2.9.1" + "@formatjs/intl" "2.10.11" + "@formatjs/intl-displaynames" "6.8.1" + "@formatjs/intl-listformat" "7.7.1" + "@types/hoist-non-react-statics" "3" + "@types/react" "^18.3.11" + hoist-non-react-statics "3" + intl-messageformat "10.7.3" + tslib "2" react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" @@ -3163,13 +3293,12 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" readdirp@~3.6.0: version "3.6.0" @@ -3331,13 +3460,12 @@ safe-regex-test@^1.0.3: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" @@ -3760,7 +3888,7 @@ ts-loader@^9.4.4: semver "^7.3.4" source-map "^0.7.4" -tslib@^2.1.0: +tslib@2, tslib@^2.1.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -3839,7 +3967,7 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" -typescript@^5.2.2: +typescript@5, typescript@^5.2.2: version "5.6.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== @@ -3859,7 +3987,7 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -undici-types@~6.19.8: +undici-types@~6.19.2, undici-types@~6.19.8: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==