From 6d21f035e5e642b6ab5844ed275baa95c31b3a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Wed, 7 Feb 2024 10:08:09 +0100 Subject: [PATCH] [#80] implement i18next --- CHANGELOG.md | 10 +- govtool/frontend/package.json | 2 + .../src/components/atoms/CopyButton.tsx | 5 +- .../src/components/atoms/StakeRadio.tsx | 12 +- .../src/components/atoms/VotingPowerChips.tsx | 11 +- .../src/components/molecules/DRepInfoCard.tsx | 4 +- .../molecules/GovernanceActionCard.tsx | 26 +- .../molecules/GovernanceActionsFilters.tsx | 5 +- .../molecules/GovernanceActionsSorting.tsx | 7 +- .../molecules/GovernanceVotedOnCard.tsx | 32 +- .../components/molecules/VoteActionForm.tsx | 57 +-- .../components/molecules/VotesSubmitted.tsx | 12 +- .../components/molecules/WalletInfoCard.tsx | 6 +- .../organisms/ChooseStakeKeyPanel.tsx | 11 +- .../organisms/ChooseWalletModal.tsx | 19 +- .../components/organisms/DashboardCards.tsx | 217 +++++----- .../DashboardGovernanceActionDetails.tsx | 19 +- .../organisms/DashboardGovernanceActions.tsx | 7 +- .../DashboardGovernanceActionsVotedOn.tsx | 12 +- .../components/organisms/DashboardTopNav.tsx | 11 +- .../organisms/DelegateTodRepStepOne.tsx | 69 ++-- .../organisms/DelegateTodRepStepTwo.tsx | 19 +- .../src/components/organisms/Drawer.tsx | 6 +- .../src/components/organisms/DrawerMobile.tsx | 5 +- .../organisms/ExternalLinkModal.tsx | 19 +- .../src/components/organisms/Footer.tsx | 7 +- .../organisms/GovernanceActionDetailsCard.tsx | 26 +- .../organisms/GovernanceActionsToVote.tsx | 4 +- .../src/components/organisms/Hero.tsx | 20 +- .../src/components/organisms/HomeCards.tsx | 25 +- .../organisms/RegisterAsdRepStepOne.tsx | 24 +- .../organisms/RegisterAsdRepStepTwo.tsx | 27 +- .../src/components/organisms/Slider.tsx | 7 +- .../src/components/organisms/StatusModal.tsx | 7 +- .../src/components/organisms/TopNav.tsx | 6 +- .../components/organisms/VotingPowerModal.tsx | 12 +- govtool/frontend/src/consts/index.ts | 1 - govtool/frontend/src/consts/texts.ts | 42 -- govtool/frontend/src/context/snackbar.tsx | 5 +- govtool/frontend/src/context/wallet.tsx | 127 +++--- .../src/hooks/forms/useDelegateTodRepForm.tsx | 14 +- .../forms/useRegisterAsdRepFormContext.tsx | 14 +- .../hooks/forms/useUpdatedRepMetadataForm.tsx | 6 +- .../forms/useUrlAndHashFormController.tsx | 27 +- .../src/hooks/forms/useVoteActionForm.tsx | 27 +- govtool/frontend/src/hooks/index.ts | 1 + govtool/frontend/src/i18n/index.ts | 16 + govtool/frontend/src/i18n/locales/en.ts | 380 ++++++++++++++++++ govtool/frontend/src/main.tsx | 13 +- .../DashboardGovernanceActionsCategory.tsx | 12 +- govtool/frontend/src/pages/DelegateTodRep.tsx | 5 +- govtool/frontend/src/pages/ErrorPage.tsx | 14 +- .../src/pages/GovernanceActionDetails.tsx | 21 +- .../frontend/src/pages/GovernanceActions.tsx | 5 +- .../src/pages/GovernanceActionsCategory.tsx | 16 +- govtool/frontend/src/pages/RegisterAsdRep.tsx | 4 +- .../frontend/src/pages/UpdatedRepMetadata.tsx | 24 +- govtool/frontend/src/types/i18next.d.ts | 11 + govtool/frontend/yarn.lock | 143 ++++--- 59 files changed, 1117 insertions(+), 579 deletions(-) delete mode 100644 govtool/frontend/src/consts/texts.ts create mode 100644 govtool/frontend/src/i18n/index.ts create mode 100644 govtool/frontend/src/i18n/locales/en.ts create mode 100644 govtool/frontend/src/types/i18next.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a65b7955b..6c21fa0e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,16 +10,14 @@ changes. ## [Unreleased] - Vitest unit tests added for utils functions [Issue 81](https://github.com/IntersectMBO/govtool/issues/81) - -## [sancho-v1.0.1](https://github.com/IntersectMBO/govtool/releases/tag/sancho-v1.0.1) 2023-12-XX - -### Added -- +- i18next library added to FE [Issue 80](https://github.com/IntersectMBO/govtool/issues/80) ### Fixed -- +- Fixed vote calculation problems related to NoConfidence DRep [Issue 59](https://github.com/IntersectMBO/govtool/issues/59) +- Fixed ada-holder/get-current-delegation error when delegated to NoConfidence or AlwaysAbstain dreps. [Issue 82](https://github.com/IntersectMBO/govtool/issues/82) ### Changed +- Changed and improved working conventions docs, PR template and codeowners file, addressing [Issue 88](https://github.com/IntersectMBO/govtool/issues/88). - Changed Node version from 8.7.1-pre to 8.7.2 and Db-sync version from sancho-2-3-0 to sancho-3-0-0. - (`docs/update-working-conventions`) Addressing [Issue 25](https://github.com/IntersectMBO/govtool/issues/25) changed working conventions documentation to improve intended flows. diff --git a/govtool/frontend/package.json b/govtool/frontend/package.json index a73d981ab..e71c3db64 100644 --- a/govtool/frontend/package.json +++ b/govtool/frontend/package.json @@ -30,11 +30,13 @@ "buffer": "^6.0.3", "date-fns": "^2.30.0", "esbuild": "^0.19.8", + "i18next": "^23.7.19", "keen-slider": "^6.8.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-gtm-module": "^2.0.11", "react-hook-form": "^7.47.0", + "react-i18next": "^14.0.1", "react-query": "^3.39.3", "react-router-dom": "^6.13.0", "storybook-addon-manual-mocks": "^1.0.3", diff --git a/govtool/frontend/src/components/atoms/CopyButton.tsx b/govtool/frontend/src/components/atoms/CopyButton.tsx index dc4eda8b9..069344c94 100644 --- a/govtool/frontend/src/components/atoms/CopyButton.tsx +++ b/govtool/frontend/src/components/atoms/CopyButton.tsx @@ -2,6 +2,7 @@ import { useMemo } from "react"; import { ICONS } from "@consts"; import { useSnackbar } from "@context"; +import { useTranslation } from "@hooks"; interface Props { isChecked?: boolean; @@ -11,6 +12,8 @@ interface Props { export const CopyButton = ({ isChecked, text, variant }: Props) => { const { addSuccessAlert } = useSnackbar(); + const { t } = useTranslation(); + const iconSrc = useMemo(() => { if (variant === "blue") { return ICONS.copyBlueIcon; @@ -29,7 +32,7 @@ export const CopyButton = ({ isChecked, text, variant }: Props) => { alt="copy" onClick={(e) => { navigator.clipboard.writeText(text); - addSuccessAlert("Copied to clipboard."); + addSuccessAlert(t("alerts.copiedToClipboard")); e.stopPropagation(); }} src={iconSrc} diff --git a/govtool/frontend/src/components/atoms/StakeRadio.tsx b/govtool/frontend/src/components/atoms/StakeRadio.tsx index cb846b68d..4fe7c584e 100644 --- a/govtool/frontend/src/components/atoms/StakeRadio.tsx +++ b/govtool/frontend/src/components/atoms/StakeRadio.tsx @@ -3,7 +3,11 @@ import { Box, IconButton, Typography } from "@mui/material"; import { ICONS } from "@consts"; import { theme } from "@/theme"; -import { useGetAdaHolderVotingPowerQuery, useScreenDimension } from "@/hooks"; +import { + useGetAdaHolderVotingPowerQuery, + useScreenDimension, + useTranslation, +} from "@hooks"; import { correctAdaFormat } from "@/utils/adaFormat"; type StakeRadioProps = { @@ -21,6 +25,7 @@ export const StakeRadio: FC = ({ ...props }) => { const { isMobile } = useScreenDimension(); const { powerIsLoading, votingPower } = useGetAdaHolderVotingPowerQuery(stakeKey); + const { t } = useTranslation(); return ( = ({ ...props }) => { - Voting power: + {t("votingPower")} {powerIsLoading ? ( - {" "} - Loading... + {t("loading")} ) : ( { const { dRep, stakeKey, isDrepLoading } = useCardano(); @@ -19,6 +19,7 @@ export const VotingPowerChips = () => { const { votingPower, powerIsLoading } = useGetAdaHolderVotingPowerQuery(stakeKey); const { isMobile, screenWidth } = useScreenDimension(); + const { t } = useTranslation(); return ( { > {dRep?.isRegistered && ( @@ -51,7 +52,7 @@ export const VotingPowerChips = () => { )} {screenWidth >= 1024 && ( - Voting power: + {t("votingPower")} )} {(dRep?.isRegistered && drepPowerIsLoading) || diff --git a/govtool/frontend/src/components/molecules/DRepInfoCard.tsx b/govtool/frontend/src/components/molecules/DRepInfoCard.tsx index 83d49f480..e438f0b63 100644 --- a/govtool/frontend/src/components/molecules/DRepInfoCard.tsx +++ b/govtool/frontend/src/components/molecules/DRepInfoCard.tsx @@ -2,15 +2,17 @@ import { Box, Typography } from "@mui/material"; import { useCardano } from "@context"; import { CopyButton } from "@atoms"; +import { useTranslation } from "@hooks"; export const DRepInfoCard = () => { const { dRepIDBech32 } = useCardano(); + const { t } = useTranslation(); return ( - My DRep ID: + {t("myDRepId")} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx index 73a50af91..56eb7429f 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx @@ -3,8 +3,7 @@ import { Box } from "@mui/material"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import { Button, Typography, Tooltip } from "@atoms"; -import { tooltips } from "@consts"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { theme } from "@/theme"; import { formatDisplayDate, @@ -43,6 +42,7 @@ export const GovernanceActionCard: FC = ({ ...props }) => { index, } = props; const { isMobile, screenWidth } = useScreenDimension(); + const { t } = useTranslation(); const { palette: { lightBlue }, @@ -85,7 +85,7 @@ export const GovernanceActionCard: FC = ({ ...props }) => { }} > - In progress + {t("inProgress")} )} @@ -101,7 +101,7 @@ export const GovernanceActionCard: FC = ({ ...props }) => { > - Governance Action Type: + {t("govActions.governanceActionType")} = ({ ...props }) => { - Governance Action ID: + {t("govActions.governanceActionId")} = ({ ...props }) => { sx={{ flexWrap: "nowrap", mr: 1 }} variant="caption" > - Submission date: + {t("govActions.submissionDate")} = ({ ...props }) => { {formatDisplayDate(createdDate)} @@ -196,7 +196,7 @@ export const GovernanceActionCard: FC = ({ ...props }) => { sx={{ flexWrap: "nowrap", mr: 1 }} variant="caption" > - Expiry date: + {t("govActions.expiryDate")} = ({ ...props }) => { {formatDisplayDate(expiryDate)} @@ -237,7 +237,7 @@ export const GovernanceActionCard: FC = ({ ...props }) => { }} data-testid={`govaction-${govActionId}-view-detail`} > - {inProgress ? "See transaction" : "View proposal details"} + {t(inProgress ? "seeTransaction" : "govActions.viewProposalDetails")} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx index 0a7426b90..ca8ae69d4 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx @@ -8,6 +8,7 @@ import { } from "@mui/material"; import { GOVERNANCE_ACTIONS_FILTERS } from "@consts"; +import { useTranslation } from "@hooks"; interface Props { chosenFilters: string[]; @@ -32,6 +33,8 @@ export const GovernanceActionsFilters = ({ [chosenFilters, setChosenFilters] ); + const { t } = useTranslation(); + return ( - Governance Action Type + {t("govActions.filterTitle")} {GOVERNANCE_ACTIONS_FILTERS.map((item) => { return ( diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsSorting.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsSorting.tsx index 9abce9ae3..b75cf3e34 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionsSorting.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionsSorting.tsx @@ -9,6 +9,7 @@ import { } from "@mui/material"; import { GOVERNANCE_ACTIONS_SORTING } from "@consts"; +import { useTranslation } from "@hooks"; interface Props { chosenSorting: string; @@ -19,6 +20,8 @@ export const GovernanceActionsSorting = ({ chosenSorting, setChosenSorting, }: Props) => { + const { t } = useTranslation(); + return ( - Sort by + {t("sortBy")} setChosenSorting("")}> - Clear + {t("clear")} diff --git a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx index 855281658..58ff325a7 100644 --- a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx @@ -5,7 +5,7 @@ import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import { Button, VotePill, Typography } from "@atoms"; import { PATHS } from "@consts"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { VotedProposal } from "@models"; import { theme } from "@/theme"; import { @@ -16,7 +16,6 @@ import { openInNewTab, } from "@utils"; import { Tooltip } from "@atoms"; -import { tooltips } from "@/consts/texts"; interface Props { votedProposal: VotedProposal; @@ -31,6 +30,7 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { palette: { lightBlue }, } = theme; const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); const proposalTypeNoEmptySpaces = getProposalTypeLabel(proposal.type).replace( / /g, @@ -77,11 +77,11 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { variant="body2" > {inProgress ? ( - "In progress" + t("inProgress") ) : ( <> - Vote submitted + {t("govActions.voteSubmitted")} )} @@ -98,7 +98,7 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { > - Governance Action Type: + {t("govActions.governanceActionType")} { - Governance Action ID: + {t("govActions.governanceActionId")} { - My Vote: + {t("govActions.myVote")} { whiteSpace: "nowrap", }} > - Vote transaction + {t("govActions.voteTransaction")} @@ -187,14 +187,14 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { py={0.75} > - Submission date: + {t("govActions.submissionDate")} {formatDisplayDate(proposal.createdDate)} @@ -219,15 +219,15 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { py={0.75} > - Expiry date: + {t("govActions.expiryDate")} {formatDisplayDate(proposal.expiryDate)} @@ -273,7 +273,7 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { }} variant="outlined" > - Change your vote + {t("govActions.changeYourVote")} diff --git a/govtool/frontend/src/components/molecules/VoteActionForm.tsx b/govtool/frontend/src/components/molecules/VoteActionForm.tsx index 27a846895..446acc9aa 100644 --- a/govtool/frontend/src/components/molecules/VoteActionForm.tsx +++ b/govtool/frontend/src/components/molecules/VoteActionForm.tsx @@ -5,7 +5,7 @@ import { Box, Link } from "@mui/material"; import { Button, Input, LoadingButton, Radio, Typography } from "@atoms"; import { ICONS } from "@consts"; import { useCardano, useModal } from "@context"; -import { useScreenDimension, useVoteActionForm } from "@hooks"; +import { useScreenDimension, useVoteActionForm, useTranslation } from "@hooks"; import { openInNewTab } from "@utils"; export const VoteActionForm = ({ @@ -24,6 +24,7 @@ export const VoteActionForm = ({ const { isMobile, screenWidth } = useScreenDimension(); const { openModal } = useModal(); const { dRep } = useCardano(); + const { t } = useTranslation(); const { setValue, @@ -57,7 +58,7 @@ export const VoteActionForm = ({ const renderCancelButton = useMemo(() => { return ( ); }, [state]); @@ -73,7 +74,7 @@ export const VoteActionForm = ({ const renderChangeVoteButton = useMemo(() => { return ( - Change vote + {t("govActions.changeVote")} ); }, [confirmVote, areFormErrors, vote, isVoteLoading]); return ( - + - Choose how you want to vote: + + {t("govActions.chooseHowToVote")} + @@ -121,7 +124,7 @@ export const VoteActionForm = ({ name="vote" register={registerInput} setValue={setValue} - title="No" + title={t("no")} value="no" /> @@ -132,14 +135,14 @@ export const VoteActionForm = ({ name="vote" register={registerInput} setValue={setValue} - title="Abstain" + title={t("abstain")} value="abstain" /> {dRep?.isRegistered && ( )} - Provide context about your vote{" "} + {t("govActions.provideContext")}{" "} - (optional) + {t("govActions.optional")} arrow {isContext && ( - + openInNewTab( "https://docs.sanchogov.tools/faqs/how-to-create-a-metadata-anchor" @@ -221,11 +224,11 @@ export const VoteActionForm = ({ } mb={isMobile ? 2 : 8} sx={{ cursor: "pointer" }} - textAlign={"center"} + textAlign="center" visibility={!isContext ? "hidden" : "visible"} > - How to create URL and hash? + {t("forms.howCreateUrlAndHash")} @@ -239,14 +242,14 @@ export const VoteActionForm = ({ }} variant="caption" > - Select a different option to change your vote + {t("govActions.selectDifferentOption")} {(state?.vote && state?.vote !== vote) || (voteFromEP && voteFromEP !== vote) ? ( {isMobile ? renderChangeVoteButton : renderCancelButton} @@ -254,7 +257,7 @@ export const VoteActionForm = ({ ) : ( - Vote + {t("govActions.vote")} )} diff --git a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx index dc7b9a199..378a75a6d 100644 --- a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx +++ b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx @@ -3,7 +3,7 @@ import { Box, Typography } from "@mui/material"; import { theme } from "@/theme"; import { VotePill } from "@atoms"; -import { useScreenDimension } from "@/hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { correctAdaFormat } from "@/utils/adaFormat"; interface Props { @@ -17,6 +17,7 @@ export const VotesSubmitted = ({ yesVotes, noVotes, abstainVotes }: Props) => { palette: { lightBlue }, } = theme; const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); return ( { style={{ marginBottom: "10px" }} /> - Votes Submitted + {t("govActions.voteSubmitted")} - for this Governance Action + {t("govActions.forGovAction")} - Votes submitted on-chain by DReps, SPOs and Constitutional Committee - members. + {t("govActions.votesSubmittedOnChain")} { marginTop: "40px", }} > - Votes: + {t("govActions.votes")} { const { address, disconnectWallet, isMainnet } = useCardano(); const navigate = useNavigate(); + const { t } = useTranslation(); const { palette: { lightBlue }, } = theme; @@ -38,7 +40,7 @@ export const WalletInfoCard = () => { - Connected Wallet: + {t("wallet.connectedWallet")} { }} sx={{ textTransform: "none", fontWeight: 500 }} > - Disconnect + {t("wallet.disconnect")} diff --git a/govtool/frontend/src/components/organisms/ChooseStakeKeyPanel.tsx b/govtool/frontend/src/components/organisms/ChooseStakeKeyPanel.tsx index fbc07b1b4..9358eda4d 100644 --- a/govtool/frontend/src/components/organisms/ChooseStakeKeyPanel.tsx +++ b/govtool/frontend/src/components/organisms/ChooseStakeKeyPanel.tsx @@ -7,7 +7,7 @@ import { useCardano, useSnackbar } from "@context"; import { PATHS } from "@consts"; import { setItemToLocalStorage, WALLET_LS_KEY } from "@utils"; import { theme } from "@/theme"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; export const ChooseStakeKeyPanel = () => { const { disconnectWallet, stakeKeys, setStakeKey } = useCardano(); @@ -15,6 +15,7 @@ export const ChooseStakeKeyPanel = () => { const { addSuccessAlert } = useSnackbar(); const [chosenKey, setChosenKey] = useState(""); const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); const { palette: { boxShadow2 }, } = theme; @@ -36,7 +37,7 @@ export const ChooseStakeKeyPanel = () => { px: isMobile ? 0 : 6, }} > - Cancel + {t("cancel")} ); }, [isMobile]); @@ -61,7 +62,7 @@ export const ChooseStakeKeyPanel = () => { }} variant="contained" > - Select + {t("select")} ); }, [isMobile, chosenKey, setStakeKey]); @@ -83,10 +84,10 @@ export const ChooseStakeKeyPanel = () => { > - Pick Stake Key + {t("wallet.pickStakeKey")} - Select the stake key you want to use: + {t("wallet.pickStakeKey")} { if (!window.cardano) return []; const keys = Object.keys(window.cardano); @@ -15,7 +18,9 @@ export function ChooseWalletModal() { const { icon, name, supportedExtensions } = window.cardano[k]; if (icon && name && supportedExtensions) { // Check if the name already exists in resultWallets - const isNameDuplicate = resultWallets.some(wallet => wallet.label === name); + const isNameDuplicate = resultWallets.some( + (wallet) => wallet.label === name + ); // Check if the supportedExtensions array contains an entry with cip === 95 const isCip95Available = Boolean( supportedExtensions?.find((i) => i.cip === 95) @@ -36,7 +41,7 @@ export function ChooseWalletModal() { return ( - Connect your Wallet + {t("wallet.connectYourWallet")} - Choose the wallet you want to connect with: + {t("wallet.chooseWallet")} - You don't have wallets to connect, install a wallet and refresh - the page and try again + {t("wallet.noWalletsToConnect")} ) : ( walletOptions.map(({ icon, label, name, cip95Available }) => { @@ -91,8 +95,7 @@ export function ChooseWalletModal() { textAlign: "center", }} > - Can’t see your wallet? Check what wallets are currently compatible - with GovTool{" "} + {t("wallet.cantSeeWalletQuestion")} - here + {t("here")} . diff --git a/govtool/frontend/src/components/organisms/DashboardCards.tsx b/govtool/frontend/src/components/organisms/DashboardCards.tsx index 85528c957..8eb669fd5 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards.tsx @@ -1,12 +1,17 @@ import { useNavigate } from "react-router-dom"; import { Box, CircularProgress, Typography } from "@mui/material"; +import { Trans } from "react-i18next"; import { IMAGES, PATHS } from "@consts"; import { useCardano, useModal } from "@context"; -import { useGetAdaHolderVotingPowerQuery, useScreenDimension } from "@hooks"; +import { + useGetAdaHolderVotingPowerQuery, + useScreenDimension, + useGetAdaHolderCurrentDelegationQuery, + useTranslation, +} from "@hooks"; import { DashboardActionCard } from "@molecules"; import { useCallback, useMemo, useState } from "react"; -import { useGetAdaHolderCurrentDelegationQuery } from "@hooks"; import { correctAdaFormat, formHexToBech32, openInNewTab } from "@utils"; export const DashboardCards = () => { @@ -32,6 +37,7 @@ export const DashboardCards = () => { useState(false); const { votingPower, powerIsLoading } = useGetAdaHolderVotingPowerQuery(stakeKey); + const { t } = useTranslation(); const retireAsDrep = useCallback(async () => { try { @@ -49,11 +55,10 @@ export const DashboardCards = () => { type: "statusModal", state: { status: "success", - title: "Retirement Transaction Submitted!", - message: - "The confirmation of your retirement might take a bit of time but you can track it using.", + title: t("modals.retirement.title"), + message: t("modals.retirement.message"), link: `https://adanordic.com/latest_transactions`, - buttonText: "Go to dashboard", + buttonText: t("modals.common.goToDashboard"), dataTestId: "retirement-transaction-submitted-modal", }, }); @@ -65,8 +70,8 @@ export const DashboardCards = () => { state: { status: "warning", message: errorMessage, - buttonText: "Go to dashboard", - title: "Oops!", + buttonText: t("modals.common.goToDashboard"), + title: t("modals.common.oops"), dataTestId: "retirement-transaction-error-modal", }, }); @@ -81,43 +86,41 @@ export const DashboardCards = () => { ]); const delegationDescription = useMemo(() => { - const correctAdaRepresentation = ( - {correctAdaFormat(votingPower)} - ); + const correctAdaRepresentation = correctAdaFormat(votingPower); if (currentDelegation === dRepID) { return ( - <> - You have delegated your voting power of ₳{correctAdaRepresentation} to - yourself. - + ); } else if (currentDelegation === "drep_always_no_confidence") { return ( - <> - You have delegated your voting power of ₳{correctAdaRepresentation}. - You are going to vote 'NO' as default. - + ); } else if (currentDelegation === "drep_always_abstain") { return ( - <> - You have delegated your voting power of ₳{correctAdaRepresentation}. - You are going to vote 'ABSTAIN' as default. - + ); } else if (currentDelegation) { return ( - <> - You have delegated your voting power of ₳{correctAdaRepresentation} to - a selected DRep. - + ); } else { return ( - <> - If you want to delegate your own voting power of ₳ - {correctAdaRepresentation}. - + ); } }, [currentDelegation, drepId, votingPower]); @@ -137,41 +140,37 @@ export const DashboardCards = () => { }, [currentDelegation, drepId, votingPower]); const progressDescription = useMemo(() => { - const correctAdaRepresentation = ( - {correctAdaFormat(votingPower)} - ); + const correctAdaRepresentation = correctAdaFormat(votingPower); if (delegateTo === dRepID) { return ( - <> - Your own voting power of ₳{correctAdaRepresentation} is in progress of - being delegated. You are going to delegate your voting power to - yourself. - + ); } if (delegateTo === "no confidence") { return ( - <> - Your own voting power of ₳{correctAdaRepresentation} is in progress of - being delegated. You are going to vote ‘NO’ as default. - + ); } if (delegateTo === "abstain") { return ( - <> - Your own voting power of ₳{correctAdaRepresentation} is in progress of - being delegated. You are going to vote ‘ABSTAIN’ as default. - + ); } if (delegateTo) { return ( - <> - Your own voting power of ₳{correctAdaRepresentation} is progress of - being delegated. You are going to delegate your voting power to a - selected DRep. - + ); } }, [delegateTo, votingPower]); @@ -214,24 +213,62 @@ export const DashboardCards = () => { formHexToBech32, ]); + const getRegistrationCardDescription = () => { + if (registerTransaction.transactionHash) { + switch (registerTransaction.type) { + case "retirement": + return t("dashboard.registration.retirementInProgress"); + case "registration": + return t("dashboard.registration.registrationInProgress"); + default: + return t("dashboard.registration.metadataUpdateInProgress"); + } + } else if (dRep?.isRegistered || dRep?.wasRegistered) { + return t("dashboard.registration.holdersCanDelegate"); + } else { + return t("dashboard.registration.ifYouWant"); + } + }; + + const getRegistrationCardTitle = () => { + if (registerTransaction?.transactionHash) { + switch (registerTransaction.type) { + case "retirement": + return t("dashboard.registration.dRepRetirement"); + case "registration": + return t("dashboard.registration.dRepRegistration"); + default: + return t("dashboard.registration.dRepUpdate"); + } + } else if (dRep?.isRegistered) { + return t("dashboard.registration.youAreRegistered"); + } else if (dRep?.wasRegistered) { + return t("dashboard.registration.registerAgain"); + } else { + return t("dashboard.registration.registerAsDRep"); + } + }; + const renderGovActionSection = useCallback(() => { return ( <> - See Active Governance Actions + {t("dashboard.headingTwo")} navigate(PATHS.dashboard_governance_actions) } - firstButtonLabel={ - dRep?.isRegistered ? "Review and vote" : "View governance actions" - } + firstButtonLabel={t( + `dashboard.govActions.${ + dRep?.isRegistered ? "reviewAndVote" : "view" + }` + )} imageURL={IMAGES.govActionListImage} - title="View Governance Actions" + title={t("dashboard.govActions.title")} /> {screenWidth < 1024 ? null : ( <> @@ -261,7 +298,7 @@ export const DashboardCards = () => { > {dRep?.isRegistered && renderGovActionSection()} - Your Participation + {t("dashboard.headingOne")} { delegateTransaction?.transactionHash ? "" : currentDelegation - ? "Change delegation" - : "Delegate" + ? t("dashboard.delegation.changeDelegation") + : t("delegate") } imageHeight={55} imageWidth={65} @@ -298,7 +335,7 @@ export const DashboardCards = () => { imageURL={IMAGES.govActionDelegateImage} cardId={displayedDelegationId} inProgress={!!delegateTransaction?.transactionHash} - cardTitle="DRep you delegated to" + cardTitle={t("dashboard.delegation.dRepDelegatedTo")} secondButtonAction={ delegateTransaction?.transactionHash ? () => openInNewTab("https://adanordic.com/latest_transactions") @@ -309,20 +346,18 @@ export const DashboardCards = () => { } secondButtonLabel={ delegateTransaction?.transactionHash - ? "See transaction" + ? t("seeTransaction") : currentDelegation ? "" - : "Learn more" + : t("learnMore") } title={ delegateTransaction?.transactionHash ? ( - "Voting Power Delegation" + t("dashboard.delegation.votingPowerDelegation") ) : currentDelegation ? ( - <> - Your Voting Power is Delegated - + ) : ( - "Use your Voting Power" + t("dashboard.delegation.useYourVotingPower") ) } /> @@ -345,19 +380,7 @@ export const DashboardCards = () => { ? "change-metadata-button" : "register-learn-more-button" } - description={ - registerTransaction.transactionHash - ? registerTransaction?.type === "retirement" - ? "The retirement process is ongoing. This may take several minutes." - : registerTransaction?.type === "registration" - ? "The registration process is ongoing. This may take several minutes." - : "The update DRep metadata is ongoing. This may take several minutes." - : dRep?.isRegistered - ? "Ada holders can delegate their voting power to you." - : dRep?.wasRegistered - ? "Ada holders can delegate their voting power to you." - : "If you want to directly participate in voting and have other ada holders delegate their voting power to you." - } + description={getRegistrationCardDescription()} firstButtonAction={ dRep?.isRegistered ? retireAsDrep @@ -367,9 +390,11 @@ export const DashboardCards = () => { firstButtonLabel={ registerTransaction?.transactionHash ? "" - : dRep?.isRegistered - ? "Retire as a DRep" - : "Register" + : t( + `dashboard.registration.${ + dRep?.isRegistered ? "retire" : "register" + }` + ) } inProgress={!!registerTransaction?.transactionHash} imageURL={IMAGES.govActionRegisterImage} @@ -387,28 +412,16 @@ export const DashboardCards = () => { } secondButtonLabel={ registerTransaction?.transactionHash - ? "See transaction" + ? t("seeTransaction") : dRep?.isRegistered - ? "Change metadata" - : "Learn more" + ? t("dashboard.registration.changeMetadata") + : t("learnMore") } cardId={dRep?.isRegistered || dRep?.wasRegistered ? drepId : ""} cardTitle={ - dRep?.isRegistered || dRep?.wasRegistered ? "My DRep ID" : "" - } - title={ - registerTransaction?.transactionHash - ? registerTransaction?.type === "retirement" - ? "DRep Retirement" - : registerTransaction?.type === "registration" - ? "DRep Registration" - : "DRep Update" - : dRep?.isRegistered - ? "You are Registered as a DRep" - : dRep?.wasRegistered - ? "Register Again as a dRep" - : "Register as a DRep" + dRep?.isRegistered || dRep?.wasRegistered ? t("myDRepId") : "" } + title={getRegistrationCardTitle()} /> {!dRep?.isRegistered && renderGovActionSection()} diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx index e952b5ab4..a15148f94 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx @@ -15,7 +15,11 @@ import { import { ICONS, PATHS } from "@consts"; import { useCardano } from "@context"; -import { useGetProposalQuery, useScreenDimension } from "@hooks"; +import { + useGetProposalQuery, + useScreenDimension, + useTranslation, +} from "@hooks"; import { GovernanceActionDetailsCard } from "@organisms"; import { formatDisplayDate, @@ -28,6 +32,7 @@ export const DashboardGovernanceActionDetails = () => { const { state, hash } = useLocation(); const navigate = useNavigate(); const { isMobile, screenWidth } = useScreenDimension(); + const { t } = useTranslation(); const { proposalId } = useParams(); const fullProposalId = proposalId + hash; @@ -45,11 +50,11 @@ export const DashboardGovernanceActionDetails = () => { style={{ textDecorationColor: "#0033AD" }} > - Governance Actions + {t("govActions.title")} , - Vote on Governance Action + {t("govActions.voteOnGovActions")} , ]; @@ -100,7 +105,7 @@ export const DashboardGovernanceActionDetails = () => { style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - Back to the list + {t("backToList")} @@ -144,12 +149,14 @@ export const DashboardGovernanceActionDetails = () => { ) : ( - Governnance action with id  + {t("govActions.withIdNotExist.partOne")}  {` ${shortenedGovActionId} `} -  does not exist. + +  {t("govActions.withIdNotExist.partTwo")} + )} diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx index 8f1a456d5..09bf1823c 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx @@ -3,7 +3,7 @@ import { Box, Tab, Tabs, styled } from "@mui/material"; import { useLocation } from "react-router-dom"; import { useCardano } from "@context"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { DataActionsBar } from "@molecules"; import { GovernanceActionsToVote, @@ -67,6 +67,7 @@ export const DashboardGovernanceActions = () => { const { dRep } = useCardano(); const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); const handleChange = (_event: React.SyntheticEvent, newValue: number) => { setContent(newValue); @@ -123,7 +124,7 @@ export const DashboardGovernanceActions = () => { > { /> { if (data.length && searchPhrase) { @@ -53,12 +58,11 @@ export const DashboardGovernanceActionsVotedOn = ({ <> {!data.length ? ( - You haven't voted on any Governance Actions yet. Check the 'To - vote on' section to vote on Governance Actions. + {t("govActions.youHaventVotedYet")} ) : !filteredData?.length ? ( - No results for the search. + {t("govActions.noResultsForTheSearch")} ) : ( <> diff --git a/govtool/frontend/src/components/organisms/DashboardTopNav.tsx b/govtool/frontend/src/components/organisms/DashboardTopNav.tsx index b3389cd38..5bf0a3be4 100644 --- a/govtool/frontend/src/components/organisms/DashboardTopNav.tsx +++ b/govtool/frontend/src/components/organisms/DashboardTopNav.tsx @@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom"; import { Box, Grid, IconButton, SwipeableDrawer } from "@mui/material"; import { Background, Link, VotingPowerChips, Typography } from "@atoms"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { ICONS, PATHS } from "@consts"; import { useCardano } from "@context"; import { DRepInfoCard, WalletInfoCard } from "@molecules"; @@ -30,6 +30,7 @@ export const DashboardTopNav = ({ const { dRep } = useCardano(); const navigate = useNavigate(); const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const { t } = useTranslation(); return ( { setIsDrawerOpen(false); @@ -126,7 +127,7 @@ export const DashboardTopNav = ({ { setIsDrawerOpen(false); @@ -138,7 +139,7 @@ export const DashboardTopNav = ({ { openInNewTab( @@ -153,7 +154,7 @@ export const DashboardTopNav = ({ { openInNewTab("https://docs.sanchogov.tools/faqs"); diff --git a/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx b/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx index 95cb0b2f0..2e333a0c1 100644 --- a/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx +++ b/govtool/frontend/src/components/organisms/DelegateTodRepStepOne.tsx @@ -9,9 +9,9 @@ import { useGetAdaHolderCurrentDelegationQuery, useGetAdaHolderVotingPowerQuery, useScreenDimension, + useTranslation, } from "@hooks"; import { theme } from "@/theme"; -import { tooltips } from "@/consts/texts"; import { correctAdaFormat } from "@utils"; interface DelegateProps { @@ -37,6 +37,7 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { palette: { boxShadow2 }, } = theme; const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); const { votingPower } = useGetAdaHolderVotingPowerQuery(stakeKey); const correctAdaRepresentation = correctAdaFormat(votingPower); @@ -46,11 +47,10 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { type: "statusModal", state: { status: "success", - title: "Delegation Transaction Submitted!", - message: - "The confirmation of your actual delegation might take a bit of time but you can track it using.", + title: t("modals.delegation.title"), + message: t("modals.delegation.message"), link: "https://adanordic.com/latest_transactions", - buttonText: "Go to dashboard", + buttonText: t("modals.common.goToDashboard"), onSubmit: () => { navigate(PATHS.dashboard); closeModal(); @@ -65,10 +65,10 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { type: "statusModal", state: { status: "warning", - title: "Oops!", + title: t("modals.delegation.title"), message: errorMessage, isWarning: true, - buttonText: "Go to dashboard", + buttonText: t("modals.common.goToDashboard"), onSubmit: () => { navigate(PATHS.dashboard); closeModal(); @@ -127,7 +127,7 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { }} variant="contained" > - {chosenOption !== dRepID ? "Next step" : "Delegate"} + {chosenOption !== dRepID ? t("nextStep") : t("delegate")} ); }, [ @@ -151,7 +151,7 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { }} variant="outlined" > - Cancel + {t("cancel")} ); }, [isMobile]); @@ -184,7 +184,7 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { > - Voting power to delegate: + {t("delegation.votingPowerToDelegate")} { )} - Use your Voting Power + {t("delegation.heading")} - You can delegate your voting power to a DRep or to a pre-defined - voting option. + {t("delegation.description")} { @@ -233,11 +232,11 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { @@ -255,7 +254,9 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { }, ]} > - Other options + + {t("delegation.otherOptions")} + arrow { <> @@ -284,11 +287,13 @@ export const DelegateTodRepStepOne = ({ setStep }: DelegateProps) => { diff --git a/govtool/frontend/src/components/organisms/DelegateTodRepStepTwo.tsx b/govtool/frontend/src/components/organisms/DelegateTodRepStepTwo.tsx index 03e0cacbf..af2f392f2 100644 --- a/govtool/frontend/src/components/organisms/DelegateTodRepStepTwo.tsx +++ b/govtool/frontend/src/components/organisms/DelegateTodRepStepTwo.tsx @@ -2,7 +2,11 @@ import { useMemo } from "react"; import { Box, Link } from "@mui/material"; import { Button, Input, LoadingButton, Typography } from "../atoms"; -import { useScreenDimension, useDelegateTodRepForm } from "@hooks"; +import { + useScreenDimension, + useDelegateTodRepForm, + useTranslation, +} from "@hooks"; import { theme } from "@/theme"; import { openInNewTab } from "@utils"; @@ -12,6 +16,7 @@ interface DelegateProps { export const DelegateTodRepStepTwo = ({ setStep }: DelegateProps) => { const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); const { palette: { boxShadow2 }, @@ -34,7 +39,7 @@ export const DelegateTodRepStepTwo = ({ setStep }: DelegateProps) => { }} variant="contained" > - Delegate + {t("delegate")} ); }, [isDelegateButtonDisabled, delegate, isMobile, isDelegationLoading]); @@ -51,7 +56,7 @@ export const DelegateTodRepStepTwo = ({ setStep }: DelegateProps) => { }} variant="outlined" > - Back + {t("back")} ); }, [isMobile]); @@ -70,20 +75,20 @@ export const DelegateTodRepStepTwo = ({ setStep }: DelegateProps) => { > - Paste DRep ID + {t("delegation.pasteDRepId")} - The DRep ID is the identifier of a DRep. + {t("delegation.dRepIdDescription")} @@ -100,7 +105,7 @@ export const DelegateTodRepStepTwo = ({ setStep }: DelegateProps) => { sx={[{ "&:hover": { cursor: "pointer" } }]} > - Where can I find a DRep ID? + {t("delegation.whereFindDRepId")} diff --git a/govtool/frontend/src/components/organisms/Drawer.tsx b/govtool/frontend/src/components/organisms/Drawer.tsx index ee8ec677f..922d920e9 100644 --- a/govtool/frontend/src/components/organisms/Drawer.tsx +++ b/govtool/frontend/src/components/organisms/Drawer.tsx @@ -6,9 +6,11 @@ import { CONNECTED_NAV_ITEMS, ICONS, IMAGES, PATHS } from "@consts"; import { useCardano } from "@context"; import { WalletInfoCard, DRepInfoCard } from "@molecules"; import { openInNewTab } from "@/utils"; +import { useTranslation } from "@hooks"; export const Drawer = () => { const { dRep } = useCardano(); + const { t } = useTranslation(); return ( @@ -65,7 +67,7 @@ export const Drawer = () => { { /> - © 2023 Voltaire Gov Tool + {t("footer.copyright")} diff --git a/govtool/frontend/src/components/organisms/DrawerMobile.tsx b/govtool/frontend/src/components/organisms/DrawerMobile.tsx index f43174244..9357c396a 100644 --- a/govtool/frontend/src/components/organisms/DrawerMobile.tsx +++ b/govtool/frontend/src/components/organisms/DrawerMobile.tsx @@ -3,7 +3,7 @@ import { Box, Grid, IconButton, SwipeableDrawer } from "@mui/material"; import { Background, Button, Link } from "../atoms"; import { ICONS, IMAGES, NAV_ITEMS } from "@consts"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { useModal } from "@context"; import { openInNewTab } from "@utils"; @@ -23,6 +23,7 @@ export const DrawerMobile = ({ }: DrawerMobileProps) => { const { screenWidth } = useScreenDimension(); const { openModal } = useModal(); + const { t } = useTranslation(); return ( - Connect your wallet + {t("wallet.connectYourWalletButton")} ) : null} diff --git a/govtool/frontend/src/components/organisms/ExternalLinkModal.tsx b/govtool/frontend/src/components/organisms/ExternalLinkModal.tsx index b0cf54d94..77d2cf509 100644 --- a/govtool/frontend/src/components/organisms/ExternalLinkModal.tsx +++ b/govtool/frontend/src/components/organisms/ExternalLinkModal.tsx @@ -3,7 +3,7 @@ import { Box, Button, Typography } from "@mui/material"; import { ModalContents, ModalHeader, ModalWrapper } from "@atoms"; import { IMAGES } from "@consts"; import { useModal } from "@context"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { theme } from "@/theme"; import { openInNewTab } from "@utils"; @@ -14,6 +14,7 @@ export interface ExternalLinkModalState { export function ExternalLinkModal() { const { state, closeModal } = useModal(); const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); const { palette: { primaryBlue, fadedPurple }, } = theme; @@ -26,13 +27,13 @@ export function ExternalLinkModal() { style={{ height: "84px", margin: "0 auto", width: "84px" }} /> - {isMobile ? "External Link Safety" : "Be Careful!"} + {t(`modals.externalLink.${isMobile ? "safety" : "beCareful"}`)} - {isMobile - ? "This is an external link:" - : "You are about to open an external link to:"} + {t( + `modals.externalLink.${isMobile ? "thisIs" : "youAreAboutToOpen"}` + )} - Exercise caution and verify the website's authenticity before sharing - personal information. To proceed, click 'Continue'. To stay on - Voltaire, click 'Cancel'. + {t("modals.externalLink.description")} - {isMobile ? "Continue" : "Continue to external link"} + {t(`${isMobile ? "continue" : "modals.externalLink.continueTo"}`)} diff --git a/govtool/frontend/src/components/organisms/Footer.tsx b/govtool/frontend/src/components/organisms/Footer.tsx index c5ce46466..a4662818c 100644 --- a/govtool/frontend/src/components/organisms/Footer.tsx +++ b/govtool/frontend/src/components/organisms/Footer.tsx @@ -1,11 +1,12 @@ import { Box, Link } from "@mui/material"; import { Typography } from "@atoms"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { openInNewTab } from "@utils"; export const Footer = () => { const { isMobile, pagePadding } = useScreenDimension(); + const { t } = useTranslation(); return ( { > - © 2023 Voltaire Gov Tool + {t("footer.copyright")} @@ -34,7 +35,7 @@ export const Footer = () => { sx={{ "&:hover": { color: "primaryBlue", cursor: "pointer" } }} variant="caption" > - Privacy policy + {t("footer.privacyPolicy")} diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx index 24c594043..af7861853 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx @@ -1,12 +1,11 @@ -import { useScreenDimension } from "@hooks"; import { Box } from "@mui/material"; import { Button, Typography } from "../atoms"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import { GovActionDetails, VoteActionForm, VotesSubmitted } from "../molecules"; import { useModal } from "@context"; +import { useScreenDimension, useTranslation } from "@hooks"; import { ICONS } from "@consts"; import { Tooltip } from "@atoms"; -import { tooltips } from "@/consts/texts"; type GovernanceActionDetailsCardProps = { abstainVotes: number; @@ -37,6 +36,7 @@ export const GovernanceActionDetailsCard = ({ }: GovernanceActionDetailsCardProps) => { const { screenWidth } = useScreenDimension(); const { openModal } = useModal(); + const { t } = useTranslation(); return ( - Submission date: + {t("govActions.submissionDate")} {createdDate} @@ -109,15 +109,15 @@ export const GovernanceActionDetailsCard = ({ width={"100%"} > - Expiry date: + {t("govActions.expiryDate")} {expiryDate} @@ -134,7 +134,7 @@ export const GovernanceActionDetailsCard = ({ - Governance Action Type: + {t("govActions.governanceActionType")} @@ -144,7 +144,7 @@ export const GovernanceActionDetailsCard = ({ - Governance Action ID: + {t("govActions.governanceActionId")} - Governance Details: + {t("govActions.details")} {typeof details === "object" && details !== null ? ( Object.entries(details).map(([key, value]) => { @@ -200,7 +200,7 @@ export const GovernanceActionDetailsCard = ({ data-testid="view-other-details-button" > - View other details + {t("govActions.viewOtherDetails")} external link 0 ? filters : defaultCategories; @@ -94,7 +96,7 @@ export const GovernanceActionsToVote = ({ <> {!mappedData.length ? ( - No results for the search + {t("govActions.noResultsForTheSearch")} ) : ( <> diff --git a/govtool/frontend/src/components/organisms/Hero.tsx b/govtool/frontend/src/components/organisms/Hero.tsx index a1a0f1bfe..4854f84bc 100644 --- a/govtool/frontend/src/components/organisms/Hero.tsx +++ b/govtool/frontend/src/components/organisms/Hero.tsx @@ -4,13 +4,14 @@ import { useNavigate } from "react-router-dom"; import { Button, Typography } from "@atoms"; import { IMAGES, PATHS } from "@consts"; import { useCardano, useModal } from "@context"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; export const Hero = () => { const { isEnabled } = useCardano(); const { openModal } = useModal(); const navigate = useNavigate(); const { isMobile, screenWidth, pagePadding } = useScreenDimension(); + const { t } = useTranslation(); const IMAGE_SIZE = screenWidth < 768 ? 140 @@ -36,24 +37,17 @@ export const Hero = () => { - SanchoNet -
- Governance Tool + {t("hero.headline")}
- Interact with SanchoNet using GovTool - a friendly user{" "} - {!isMobile &&
} - interface connected to SanchoNet. You can delegate{" "} - {!isMobile &&
} - your voting power (tAda) or become a SanchoNet DRep{" "} - {!isMobile &&
} - to allow people to delegate voting power to you. + {t("hero.description")}
{ const navigate = useNavigate(); const { openModal } = useModal(); const { isMobile, screenWidth } = useScreenDimension(); + const { t } = useTranslation(); return ( { openModal({ type: "chooseWallet" })} - firstButtonLabel="Connect to delegate" + firstButtonLabel={t("dashboard.delegation.connectToDelegate")} imageHeight={80} imageURL={IMAGES.govActionDelegateImage} imageWidth={115} @@ -48,8 +49,8 @@ export const HomeCards = () => { "https://docs.sanchogov.tools/faqs/ways-to-use-your-voting-power" ) } - secondButtonLabel="Learn more" - title="Use your Voting Power" + secondButtonLabel={t("learnMore")} + title={t("dashboard.delegation.useYourVotingPower")} /> { openModal({ type: "chooseWallet" })} - firstButtonLabel="Connect to register" + firstButtonLabel={t("dashboard.registration.connectToRegister")} imageHeight={80} imageURL={IMAGES.govActionRegisterImage} imageWidth={70} @@ -70,8 +71,8 @@ export const HomeCards = () => { "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep" ) } - secondButtonLabel="Learn more" - title="Register as a DRep" + secondButtonLabel={t("learnMore")} + title={t("dashboard.registration.registerAsDRep")} /> { > navigate(PATHS.governance_actions)} - firstButtonLabel="View governance actions" + firstButtonLabel={t("dashboard.govActions.view")} imageHeight={80} imageURL={IMAGES.govActionListImage} imageWidth={80} - title="Governance Actions" + title={t("dashboard.govActions.title")} /> diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx b/govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx index df466b4db..246ac0f86 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx @@ -4,7 +4,11 @@ import { Box, Link } from "@mui/material"; import { Button, Input, Typography } from "@atoms"; import { PATHS } from "@consts"; -import { useScreenDimension, useRegisterAsdRepFormContext } from "@hooks"; +import { + useScreenDimension, + useRegisterAsdRepFormContext, + useTranslation, +} from "@hooks"; import { theme } from "@/theme"; import { openInNewTab } from "@utils"; @@ -14,6 +18,7 @@ interface Props { export const RegisterAsdRepStepOne = ({ setStep }: Props) => { const navigate = useNavigate(); + const { t } = useTranslation(); const { palette: { boxShadow2 }, } = theme; @@ -33,7 +38,7 @@ export const RegisterAsdRepStepOne = ({ setStep }: Props) => { }} variant="outlined" > - Cancel + {t("cancel")} ); }, [isMobile]); @@ -53,7 +58,7 @@ export const RegisterAsdRepStepOne = ({ setStep }: Props) => { }} variant="contained" > - {showSubmitButton ? "Confirm" : "Skip"} + {showSubmitButton ? t("confirm") : t("skip")} ); }, [isMobile, isValid, showSubmitButton]); @@ -74,23 +79,22 @@ export const RegisterAsdRepStepOne = ({ setStep }: Props) => { sx={{ letterSpacing: 1.5, textAlign: "center" }} variant="body1" > - OPTIONAL + {t("registration.optional")} - Add Information + {t("registration.headingStepOne")} - You can include extra information about yourself by adding a URL and - its hash. + {t("registration.descriptionStepOne")} { { sx={{ cursor: "pointer" }} > - How to create URL and hash? + {t("forms.howCreateUrlAndHash")}
diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx b/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx index 60ddbdd85..b735d50b3 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx @@ -3,7 +3,11 @@ import { Box } from "@mui/material"; import { LoadingButton, Button, Typography } from "@atoms"; import { theme } from "@/theme"; -import { useRegisterAsdRepFormContext, useScreenDimension } from "@hooks"; +import { + useRegisterAsdRepFormContext, + useScreenDimension, + useTranslation, +} from "@hooks"; interface Props { setStep: Dispatch>; @@ -15,6 +19,7 @@ export const RegisterAsdRepStepTwo = ({ setStep }: Props) => { } = theme; const { isLoading, submitForm } = useRegisterAsdRepFormContext(); const { isMobile, pagePadding, screenWidth } = useScreenDimension(); + const { t } = useTranslation(); const renderBackButton = useMemo(() => { return ( @@ -28,7 +33,7 @@ export const RegisterAsdRepStepTwo = ({ setStep }: Props) => { }} variant="outlined" > - Back + {t("back")} ); }, [isMobile]); @@ -48,7 +53,7 @@ export const RegisterAsdRepStepTwo = ({ setStep }: Props) => { }} variant="contained" > - Register + {t("registration.register")} ); }, [isLoading, isMobile, submitForm]); @@ -65,19 +70,19 @@ export const RegisterAsdRepStepTwo = ({ setStep }: Props) => { > - Confirm DRep registration + {t("registration.headingStepTwo")} - By clicking register you create your DRep ID within your wallet and - become a DRep.
-
- Once the registration has completed your DRep ID will be shown on your - dashboard. You will be able to share your DRep ID so that other ada - holders can delegate their voting power to you. + {t("registration.descriptionStepTwo")}
- Show all + {t("slider.showAll")} )} @@ -171,7 +172,7 @@ export const Slider = ({ } > - View all + {t("slider.viewAll")} (); const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); return ( @@ -49,7 +50,7 @@ export function StatusModal() { target="_blank" sx={[{ "&:hover": { cursor: "pointer" } }]} > - this link + {t("thisLink")} )} @@ -66,7 +67,7 @@ export function StatusModal() { }} variant="contained" > - {state?.buttonText || "Confirm"} + {state?.buttonText || t("confirm")} ); diff --git a/govtool/frontend/src/components/organisms/TopNav.tsx b/govtool/frontend/src/components/organisms/TopNav.tsx index 56b0cb104..229ba6526 100644 --- a/govtool/frontend/src/components/organisms/TopNav.tsx +++ b/govtool/frontend/src/components/organisms/TopNav.tsx @@ -7,6 +7,7 @@ import { ICONS, IMAGES, PATHS, NAV_ITEMS } from "@consts"; import { useCardano, useModal } from "@context"; import { useScreenDimension } from "@hooks"; import { openInNewTab } from "@utils"; +import { useTranslation } from "@hooks"; import { DrawerMobile } from "./DrawerMobile"; @@ -19,6 +20,7 @@ export const TopNav = ({ isConnectButton = true }) => { const { screenWidth, isMobile } = useScreenDimension(); const { isEnabled, disconnectWallet, stakeKey } = useCardano(); const navigate = useNavigate(); + const { t } = useTranslation(); useEffect(() => { const onScroll = () => { @@ -104,7 +106,7 @@ export const TopNav = ({ isConnectButton = true }) => { size="extraLarge" variant="contained" > - Connect wallet + {t("wallet.connectWallet")}
) : null} @@ -126,7 +128,7 @@ export const TopNav = ({ isConnectButton = true }) => { }} variant="contained" > - Connect + {t("wallet.connect")} ) : null} (); + const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); + const VOTES = [ { title: "yes", vote: state?.yesVotes }, { title: "abstain", vote: state?.abstainVotes }, { title: "no", vote: state?.noVotes }, ]; - const { isMobile } = useScreenDimension(); return ( - Governance Action votes + {t("modals.votingPower.govActionsVotes")} - Votes submitted by DReps + {t("modals.votingPower.votesSubmittedByDReps")} {VOTES.map((vote, index) => ( - Your vote + {t("modals.votingPower.yourVote")} ) : null} ([]); const [{ messageInfo, open }, setState] = useState(defaultState); const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); const addWarningAlert = useCallback( (message: string, autoHideDuration = DEFAULT_AUTO_HIDE_DURATION) => @@ -97,7 +98,7 @@ function SnackbarProvider({ children }: ProviderProps) { ); const addChangesSavedAlert = useCallback( - () => addSuccessAlert("Your changes have been saved"), + () => addSuccessAlert(t("alerts.changesSaved")), [addSuccessAlert] ); diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index 3d15fb593..19e46a92b 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -45,6 +45,7 @@ import { Buffer } from "buffer"; import { useNavigate } from "react-router-dom"; import { Link } from "@mui/material"; import * as Sentry from "@sentry/react"; +import { Trans } from "react-i18next"; import { useModal, useSnackbar } from "."; @@ -71,6 +72,7 @@ import { setLimitedDelegationInterval, setLimitedRegistrationInterval, } from "./walletUtils"; +import { useTranslation } from "@hooks"; interface Props { children: React.ReactNode; @@ -203,6 +205,7 @@ function CardanoProvider(props: Props) { const [isDrepLoading, setIsDrepLoading] = useState(true); const { addSuccessAlert, addWarningAlert, addErrorAlert } = useSnackbar(); + const { t } = useTranslation(); const isPendingTransaction = useCallback(() => { if ( @@ -214,10 +217,9 @@ function CardanoProvider(props: Props) { type: "statusModal", state: { status: "info", - title: "Please wait for your previous transaction to be completed.", - message: - "Before performing a new action please wait for the previous action transaction to be completed.", - buttonText: "Ok", + title: t("modals.waitForTransaction.title"), + message: t("modals.waitForTransaction.message"), + buttonText: t("ok"), onSubmit: () => { closeModal(); }, @@ -286,13 +288,9 @@ function CardanoProvider(props: Props) { stakeKey ).then((isDelegated) => { if (isDelegated) { - addSuccessAlert( - "Your voting power has been successfully delegated!" - ); + addSuccessAlert(t("alerts.delegation.success")); } else { - addWarningAlert( - "Your voting power has been successfully delegated! Please refresh the page." - ); + addWarningAlert(t("alerts.delegation.refreshPage")); } }); } @@ -303,7 +301,7 @@ function CardanoProvider(props: Props) { TIME_TO_EXPIRE_TRANSACTION ) { resetDelegateTransaction(); - if (isEnabled) addErrorAlert("Delegation transaction failed"); + if (isEnabled) addErrorAlert(t("alerts.delegation.failed")); } }; let interval = setInterval(checkDelegateTransaction, REFRESH_TIME); @@ -338,28 +336,20 @@ function CardanoProvider(props: Props) { ).then((isRegistered) => { if (registerTransaction.type === "registration") { if (isRegistered) { - addSuccessAlert( - "You have successfully registered as a DRep!" - ); + addSuccessAlert(t("alerts.registration.success")); } else { - addWarningAlert( - "You have successfully registered as a DRep! Please refresh the page." - ); + addWarningAlert(t("alerts.registration.refreshPage")); } } else if (registerTransaction.type === "retirement") { if (!isRegistered) { - addSuccessAlert( - "You have successfully retired from being a DRep!" - ); + addSuccessAlert(t("alerts.retirement.success")); } else { - addWarningAlert( - "You have successfully retired from being a DRep! Please refresh the page." - ); + addWarningAlert(t("alerts.retirement.refreshPage")); } } }); } else { - addSuccessAlert("You have successfully updated DRep metadata!"); + addSuccessAlert(t("alerts.metadataUpdate.success")); } } resetRegisterTransaction(); @@ -371,11 +361,15 @@ function CardanoProvider(props: Props) { resetRegisterTransaction(); if (isEnabled) addErrorAlert( - registerTransaction.type === "retirement" - ? "Retirement transaction failed" - : registerTransaction.type === "registration" - ? "Registration transaction failed" - : "Update DRep metadata transaction failed" + t( + `alerts.${ + registerTransaction.type === "retirement" + ? "retirement.failed" + : registerTransaction.type === "registration" + ? "registration.failed" + : "metadataUpdate.failed" + }` + ) ); } }; @@ -398,14 +392,14 @@ function CardanoProvider(props: Props) { ); if (status.transactionConfirmed) { resetVoteTransaction(); - if (isEnabled) addSuccessAlert("You have successfully voted!"); + if (isEnabled) addSuccessAlert(t("alerts.voting.success")); } if ( new Date().getTime() - new Date(voteTransaction?.time).getTime() > TIME_TO_EXPIRE_TRANSACTION ) { resetVoteTransaction(); - if (isEnabled) addErrorAlert("Vote transaction failed"); + if (isEnabled) addErrorAlert(t("alerts.voting.failed")); } }; let interval = setInterval(checkVoteTransaction, REFRESH_TIME); @@ -417,7 +411,7 @@ function CardanoProvider(props: Props) { registerTransaction?.transactionHash || delegateTransaction?.transactionHash) ) { - addWarningAlert("Transaction in progress. Please wait.", 10000); + addWarningAlert(t("alerts.transactionInProgress"), 10000); } }, [delegateTransaction, registerTransaction, voteTransaction]); @@ -530,15 +524,13 @@ function CardanoProvider(props: Props) { try { // Check that this wallet supports CIP-95 connection if (!window.cardano[walletName].supportedExtensions) { - throw new Error("Your wallet does not support CIP-30 extensions."); + throw new Error(t("errors.walletNoCIP30Support")); } else if ( !window.cardano[walletName].supportedExtensions.some( (item) => item.cip === 95 ) ) { - throw new Error( - "Your wallet does not support the required CIP-30 extension, CIP-95." - ); + throw new Error(t("errors.walletNoCIP30Nor90Support")); } // Enable wallet connection const enabledApi = await window.cardano[walletName] @@ -556,18 +548,15 @@ function CardanoProvider(props: Props) { // Check if wallet has enabled the CIP-95 extension const enabledExtensions = await enabledApi.getExtensions(); if (!enabledExtensions.some((item) => item.cip === 95)) { - throw new Error( - "Your wallet did not enable the needed CIP-95 functions during connection." - ); + throw new Error(t("errors.walletNoCIP90FunctionsEnabled")); } const network = await enabledApi.getNetworkId(); if (network != NETWORK) { throw new Error( - `You are trying to connect with a wallet connected to ${ - network == 1 ? "mainnet" : "testnet" - }. Please adjust your wallet settings to connect to ${ - network != 1 ? "mainnet" : "testnet" - } or select a different wallet` + t("errors.tryingConnectTo", { + networkFrom: network == 1 ? "mainnet" : "testnet", + networkTo: network != 1 ? "mainnet" : "testnet", + }) ); } setIsMainnet(network == 1); @@ -575,7 +564,7 @@ function CardanoProvider(props: Props) { const usedAddresses = await enabledApi.getUsedAddresses(); const unusedAddresses = await enabledApi.getUnusedAddresses(); if (!usedAddresses.length && !unusedAddresses.length) { - throw new Error("No addresses found."); + throw new Error(t("errors.noAddressesFound")); } if (!usedAddresses.length) { setAddress(unusedAddresses[0]); @@ -605,9 +594,7 @@ function CardanoProvider(props: Props) { .to_hex(); }); } else { - console.log( - "Warning, no registered stake keys, using unregistered stake keys" - ); + console.warn(t("warnings.usingUnregisteredStakeKeys")); stakeKeysList = unregisteredStakeKeysList.map((stakeKey) => { const stakeKeyHash = PublicKey.from_hex(stakeKey).hash(); const stakeCredential = Credential.from_keyhash(stakeKeyHash); @@ -649,7 +636,7 @@ function CardanoProvider(props: Props) { const protocol = await getEpochParams(); setItemToLocalStorage(PROTOCOL_PARAMS_KEY, protocol); - return { status: "OK", stakeKey: stakeKeySet }; + return { status: t("ok"), stakeKey: stakeKeySet }; } catch (e) { Sentry.captureException(e); console.error(e); @@ -661,11 +648,11 @@ function CardanoProvider(props: Props) { setIsEnabled(false); throw { status: "ERROR", - error: `${e == undefined ? "Something went wrong" : e}`, + error: `${e == undefined ? t("errors.somethingWentWrong") : e}`, }; } } - throw { status: "ERROR", error: `Something went wrong` }; + throw { status: "ERROR", error: t("errors.somethingWentWrong") }; }, [isEnabled, stakeKeys] ); @@ -741,7 +728,7 @@ function CardanoProvider(props: Props) { const txBuilder = await initTransactionBuilder(); if (!txBuilder) { - throw new Error("Application can not create transaction"); + throw new Error(t("errors.appCannotCreateTransaction")); } if (certBuilder) { @@ -757,7 +744,7 @@ function CardanoProvider(props: Props) { !walletState.usedAddress || !walletApi ) - throw new Error("Check the wallet is connected."); + throw new Error(t("errors.checkIsWalletConnected")); const shelleyOutputAddress = Address.from_bech32( walletState.usedAddress ); @@ -781,7 +768,7 @@ function CardanoProvider(props: Props) { const utxos = await getUtxos(walletApi); if (!utxos) { - throw new Error("Application can not get utxos"); + throw new Error(t("errors.appCannotGetUtxos")); } // Find the available UTXOs in the wallet and use them as Inputs for the transaction const txUnspentOutputs = await getTxUnspentOutputs(utxos); @@ -915,7 +902,7 @@ function CardanoProvider(props: Props) { const certBuilder = CertificatesBuilder.new(); let stakeCred; if (!stakeKey) { - throw new Error("No stake key selected"); + throw new Error(t("errors.noStakeKeySelected")); } // Remove network tag from stake key hash const stakeKeyHash = Ed25519KeyHash.from_hex(stakeKey.substring(2)); @@ -923,7 +910,7 @@ function CardanoProvider(props: Props) { if (registeredStakeKeysListState.length > 0) { stakeCred = Credential.from_keyhash(stakeKeyHash); } else { - console.log("Registering stake key"); + console.log(t("errors.registeringStakeKey")); stakeCred = Credential.from_keyhash(stakeKeyHash); const stakeKeyRegCert = StakeRegistration.new(stakeCred); certBuilder.add(Certificate.new_stake_registration(stakeKeyRegCert)); @@ -986,7 +973,7 @@ function CardanoProvider(props: Props) { anchor ); } else { - console.log("DRep Registration - not using anchor"); + console.log(t("errors.notUsingAnchor")); dRepRegCert = DrepRegistration.new( dRepCred, BigNum.from_str(`${epochParams.drep_deposit}`) @@ -1197,9 +1184,10 @@ function useCardano() { const { openModal, closeModal } = useModal(); const { addSuccessAlert } = useSnackbar(); const navigate = useNavigate(); + const { t } = useTranslation(); if (context === undefined) { - throw new Error("useCardano must be used within a CardanoProvider"); + throw new Error(t("errors.useCardano")); } const enable = useCallback( @@ -1212,7 +1200,7 @@ function useCardano() { if (!result.error) { closeModal(); if (result.stakeKey) { - addSuccessAlert(`Wallet connected`, 3000); + addSuccessAlert(t("alerts.walletConnected"), 3000); } if (!isSanchoInfoShown) { openModal({ @@ -1222,23 +1210,26 @@ function useCardano() { dataTestId: "info-about-sancho-net-modal", message: (

- The SanchoNet GovTool is currently in beta and it connects - to{" "} + {t("system.sanchoNetIsBeta")} openInNewTab("https://sancho.network/")} sx={{ cursor: "pointer" }} > - SanchoNet + {t("system.sanchoNet")} .
-
Please note, this tool uses ‘Test ada’ - NOT real ada. All - governance actions and related terms pertain to SanchoNet." +
+ , + ]} + />

), - title: "This tool is connected to SanchoNet", - buttonText: "Ok", + title: t("system.toolConnectedToSanchonet"), + buttonText: t("ok"), }, }); setItemToLocalStorage(SANCHO_INFO_KEY + `_${walletName}`, true); @@ -1257,7 +1248,7 @@ function useCardano() { onSubmit: () => { closeModal(); }, - title: "Oops!", + title: t("modals.common.oops"), dataTestId: "wallet-connection-error-modal", }, }); diff --git a/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx b/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx index 1493d1f34..6b89c24ef 100644 --- a/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx @@ -4,7 +4,7 @@ import { useForm, useWatch } from "react-hook-form"; import { PATHS } from "@consts"; import { useCardano, useModal } from "@context"; -import { useGetDRepListQuery } from "@hooks"; +import { useGetDRepListQuery, useTranslation } from "@hooks"; import { formHexToBech32 } from "@utils"; export interface DelegateTodrepFormValues { @@ -21,6 +21,7 @@ export const useDelegateTodRepForm = () => { const { openModal, closeModal, modal } = useModal(); const [isLoading, setIsLoading] = useState(false); const navigate = useNavigate(); + const { t } = useTranslation(); const { control, handleSubmit } = useForm(); @@ -43,7 +44,7 @@ export const useDelegateTodRepForm = () => { }); } if (!drepList?.length || !isValidDrep) { - throw new Error("DrepId not found"); + throw new Error(t("errors.dRepIdNotFound")); } const certBuilder = await buildVoteDelegationCert(dRepId); const result = await buildSignSubmitConwayCertTx({ @@ -55,11 +56,10 @@ export const useDelegateTodRepForm = () => { type: "statusModal", state: { status: "success", - title: "Delegation Transaction Submitted!", - message: - "The confirmation of your actual delegation might take a bit of time but you can track it using.", + title: t("modals.delegation.title"), + message: t("modals.delegation.message"), link: "https://adanordic.com/latest_transactions", - buttonText: "Go to dashboard", + buttonText: t("modals.common.goToDashboard"), onSubmit: () => { navigate(PATHS.dashboard); closeModal(); @@ -76,7 +76,7 @@ export const useDelegateTodRepForm = () => { onSubmit: () => { closeModal(); }, - title: "Oops!", + title: t("modals.common.oops"), dataTestId: "delegation-transaction-error-modal", }, }); diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx index a68b89519..27a9fc6a5 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx @@ -4,13 +4,14 @@ import { useFormContext, useWatch } from "react-hook-form"; import { PATHS } from "@consts"; import { useCardano, useModal } from "@context"; -import { UrlAndHashFormValues } from "@hooks"; +import { UrlAndHashFormValues, useTranslation } from "@hooks"; export const useRegisterAsdRepFormContext = () => { const { buildSignSubmitConwayCertTx, buildDRepRegCert } = useCardano(); const { openModal, closeModal } = useModal(); const [isLoading, setIsLoading] = useState(false); const navigate = useNavigate(); + const { t } = useTranslation(); const { control, @@ -49,11 +50,10 @@ export const useRegisterAsdRepFormContext = () => { type: "statusModal", state: { status: "success", - title: "Registration Transaction Submitted!", - message: - "The confirmation of your registration might take a bit of time but you can track it using.", + title: t("modals.registration.title"), + message: t("modals.registration.message"), link: "https://adanordic.com/latest_transactions", - buttonText: "Go to dashboard", + buttonText: t("modals.common.goToDashboard"), onSubmit: () => { navigate(PATHS.dashboard); closeModal(); @@ -68,9 +68,9 @@ export const useRegisterAsdRepFormContext = () => { type: "statusModal", state: { status: "warning", - title: "Oops!", + title: t("modals.common.oops"), message: errorMessage, - buttonText: "Go to dashboard", + buttonText: t("modals.common.goToDashboard"), onSubmit: () => { navigate(PATHS.dashboard); closeModal(); diff --git a/govtool/frontend/src/hooks/forms/useUpdatedRepMetadataForm.tsx b/govtool/frontend/src/hooks/forms/useUpdatedRepMetadataForm.tsx index da5adae9f..da48b2250 100644 --- a/govtool/frontend/src/hooks/forms/useUpdatedRepMetadataForm.tsx +++ b/govtool/frontend/src/hooks/forms/useUpdatedRepMetadataForm.tsx @@ -7,12 +7,14 @@ import { useNavigate } from "react-router-dom"; import { PATHS } from "@consts"; import { useCardano, useSnackbar } from "@context"; +import { useTranslation } from "@hooks"; export const useUpdatedRepMetadataForm = () => { const { buildSignSubmitConwayCertTx, buildDRepUpdateCert } = useCardano(); const { addSuccessAlert, addErrorAlert } = useSnackbar(); const [isLoading, setIsLoading] = useState(false); const navigate = useNavigate(); + const { t } = useTranslation(); const { handleSubmit, @@ -37,10 +39,10 @@ export const useUpdatedRepMetadataForm = () => { type: "registration", registrationType: "update", }); - if (result) addSuccessAlert("Metadata update submitted"); + if (result) addSuccessAlert(t("alerts.metadataUpdate.success")); navigate(PATHS.dashboard); } catch (e) { - addErrorAlert("Something went wrong while updating metadata"); + addErrorAlert(t("alerts.metadataUpdate.failed")); } finally { setIsLoading(false); } diff --git a/govtool/frontend/src/hooks/forms/useUrlAndHashFormController.tsx b/govtool/frontend/src/hooks/forms/useUrlAndHashFormController.tsx index 437354e8a..062f8b566 100644 --- a/govtool/frontend/src/hooks/forms/useUrlAndHashFormController.tsx +++ b/govtool/frontend/src/hooks/forms/useUrlAndHashFormController.tsx @@ -3,6 +3,7 @@ import { yupResolver } from "@hookform/resolvers/yup"; import { useForm } from "react-hook-form"; import * as Yup from "yup"; import { HASH_REGEX, URL_REGEX } from "@utils"; +import { useTranslation } from "@hooks"; export interface UrlAndHashFormValues { url?: string; @@ -10,27 +11,37 @@ export interface UrlAndHashFormValues { } export const useUrlAndHashFormController = () => { + const { t } = useTranslation(); + const validationSchema = useMemo( () => Yup.object().shape({ url: Yup.string() .trim() - .max(64, "Url must be less than 65 characters") - .test("url-validation", "Invalid URL format", (value) => { - return !value || URL_REGEX.test(value); - }), + .max(64, t("forms.errors.urlTooLong")) + .test( + "url-validation", + t("forms.errors.urlInvalidFormat"), + (value) => { + return !value || URL_REGEX.test(value); + } + ), hash: Yup.string() .trim() .test( "hash-length-validation", - "Hash must be exactly 64 characters long", + t("forms.errors.hashInvalidLength"), (value) => { return !value || value.length === 64; } ) - .test("hash-format-validation", "Invalid hash format", (value) => { - return !value || HASH_REGEX.test(value); - }), + .test( + "hash-format-validation", + t("forms.errors.hashInvalidFormat"), + (value) => { + return !value || HASH_REGEX.test(value); + } + ), }), [] ); diff --git a/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx b/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx index bd12705e6..cf1a0cbe0 100644 --- a/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx +++ b/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx @@ -9,34 +9,45 @@ import { UrlAndHashFormValues } from "./useUrlAndHashFormController"; import { PATHS } from "@consts"; import { useCardano, useSnackbar } from "@context"; import { HASH_REGEX, URL_REGEX } from "@utils"; +import { useTranslation } from "@hooks"; export interface VoteActionFormValues extends UrlAndHashFormValues { vote: string; } export const useVoteActionFormController = () => { + const { t } = useTranslation(); + const validationSchema = useMemo( () => Yup.object().shape({ vote: Yup.string().oneOf(["yes", "no", "abstain"]).required(), url: Yup.string() .trim() - .max(64, "Url must be less than 65 characters") - .test("url-validation", "Invalid URL format", (value) => { - return !value || URL_REGEX.test(value); - }), + .max(64, t("forms.errors.urlTooLong")) + .test( + "url-validation", + t("forms.errors.urlInvalidFormat"), + (value) => { + return !value || URL_REGEX.test(value); + } + ), hash: Yup.string() .trim() .test( "hash-length-validation", - "Hash must be exactly 64 characters long", + t("forms.errors.hashInvalidLength"), (value) => { return !value || value.length === 64; } ) - .test("hash-format-validation", "Invalid hash format", (value) => { - return !value || HASH_REGEX.test(value); - }), + .test( + "hash-format-validation", + t("forms.errors.hashInvalidFormat"), + (value) => { + return !value || HASH_REGEX.test(value); + } + ), }), [] ); diff --git a/govtool/frontend/src/hooks/index.ts b/govtool/frontend/src/hooks/index.ts index 04d84bc47..e17b56d3a 100644 --- a/govtool/frontend/src/hooks/index.ts +++ b/govtool/frontend/src/hooks/index.ts @@ -1,3 +1,4 @@ +export { useTranslation } from "react-i18next"; export * from "./useScreenDimension"; export * from "./useSlider"; export * from "./useSaveScrollPosition"; diff --git a/govtool/frontend/src/i18n/index.ts b/govtool/frontend/src/i18n/index.ts new file mode 100644 index 000000000..035c38ccc --- /dev/null +++ b/govtool/frontend/src/i18n/index.ts @@ -0,0 +1,16 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; + +import { en } from "./locales/en"; + +i18n.use(initReactI18next).init({ + resources: { + en, + }, + fallbackLng: "en", + interpolation: { + escapeValue: false, + }, +}); + +export default i18n; diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts new file mode 100644 index 000000000..876dd71b0 --- /dev/null +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -0,0 +1,380 @@ +export const en = { + translation: { + alerts: { + delegation: { + failed: "Delegation transaction failed", + refreshPage: + "Your voting power has been successfully delegated! Please refresh the page.", + success: "Your voting power has been successfully delegated!", + }, + metadataUpdate: { + failed: "Update DRep metadata transaction failed", + success: "You have successfully updated DRep metadata!", + }, + registration: { + failed: "Registration transaction failed", + refreshPage: + "You have successfully registered as a DRep! Please refresh the page.", + success: "You have successfully registered as a DRep!", + }, + retirement: { + failed: "Retirement transaction failed", + refreshPage: + "You have successfully retired from being a DRep! Please refresh the page.", + success: "You have successfully retired from being a DRep!", + }, + voting: { + failed: "Vote transaction failed", + success: "You have successfully voted!", + }, + changesSaved: "Your changes have been saved", + copiedToClipboard: "Copied to clipboard", + transactionInProgress: "Transaction in progress. Please wait.", + walletConnected: "Wallet connected", + }, + dashboard: { + headingOne: "Your Participation", + headingTwo: "See Active Governance Actions", + delegation: { + changeDelegation: "Change delegation", + connectToDelegate: "Connect to delegate", + delegateOwnPower: + "If you want to delegate your own voting power of ₳{{ada}}.", + description: + "If you want to delegate to a DRep or select a default option.", + dRepDelegatedTo: "DRep you delegated to", + toDRep: + "You have delegated your voting power of ₳{{ada}} to a selected DRep.", + toYourself: + "You have delegated your voting power of ₳{{ada}} to yourself.", + useYourVotingPower: "Use your Voting Power", + voteAbstain: + "You have delegated your voting power of ₳{{ada}}. You are going to vote 'ABSTAIN' as default.", + voteNo: + "You have delegated your voting power of ₳{{ada}}. You are going to vote 'NO' as default.", + votingPowerDelegation: "Voting Power Delegation", + yourVotingPowerIsDelegated: + "Your Voting Power is Delegated", + inProgress: { + toDRep: + "Your own voting power of ₳{{ada}} is progress of being delegated. You are going to delegate your voting power to a selected DRep.", + toYourself: + "Your own voting power of ₳{{ada}} is in progress of being delegated. You are going to delegate your voting power to yourself.", + voteAbstain: + "Your own voting power of ₳{{ada}} is in progress of being delegated. You are going to vote ‘ABSTAIN’ as default.", + voteNo: + "Your own voting power of ₳{{ada}} is in progress of being delegated. You are going to vote ‘NO’ as default.", + }, + }, + govActions: { + description: "Review governance actions submitted on-chain.", + reviewAndVote: "Review and vote", + title: "Governance Actions", + view: "View governance actions", + }, + registration: { + changeMetadata: "Change metadata", + connectToRegister: "Connect to register", + dRepRegistration: "DRep Registration", + dRepRetirement: "DRep Retirement", + dRepUpdate: "DRep Update", + description: + "If you want to directly participate in voting and have other ada holders delegate their voting power to you.", + holdersCanDelegate: + "Ada holders can delegate their voting power to you.", + ifYouWant: + "If you want to directly participate in voting and have other ada holders delegate their voting power to you.", + metadataUpdateInProgress: + "The update DRep metadata is ongoing. This may take several minutes.", + register: "Register", + registerAgain: "Register Again as a dRep", + registerAsDRep: "Register as a DRep", + registrationInProgress: + "The registration process is ongoing. This may take several minutes.", + retire: "Retire as a DRep", + retirementInProgress: + "The retirement process is ongoing. This may take several minutes.", + youAreRegistered: "You are Registered as a DRep", + }, + }, + delegation: { + description: + "You can delegate your voting power to a DRep or to a pre-defined voting option.", + dRepIdDescription: "The DRep ID is the identifier of a DRep.", + heading: "Use your Voting Power", + otherOptions: "Other options", + pasteDRepId: "Paste DRep ID", + votingPowerToDelegate: "Voting power to delegate:", + whereFindDRepId: "The DRep ID is the identifier of a DRep.", + abstain: { + subtitle: "Select this to vote ABSTAIN to every vote.", + title: "Vote ABSTAIN as default", + }, + noConfidence: { + subtitle: + "Select this to signal no confidence in the current constitutional committee by voting NO on every proposal and voting YES to no-confidence proposals.", + title: "Signal no confidence", + }, + toDRep: { + subtitle: + "Select this to delegate to a DRep using their related DRep ID.", + title: "Delegate to DRep", + }, + toMyself: { + subtitle: "Select this to delegate your own voting power to yourself.", + title: "Delegate to myself", + }, + }, + errorPage: { + backToDashboard: "Back to dashboard", + backToHomepage: "Back to homepage", + error: "Error ", + serverError: "We have an internal server error.", + whoops: "Whoops!", + }, + errors: { + appCannotCreateTransaction: "Application can not create transaction.", + appCannotGetUtxos: "Application can not get utxos", + checkIsWalletConnected: "Check if the wallet is connected.", + dRepIdNotFound: "DrepId not found", + notUsingAnchor: "DRep Registration - not using anchor", + noAddressesFound: "No addresses found", + noStakeKeySelected: "No stake key selected", + registeringStakeKey: "Registering stake key", + somethingWentWrong: "Something went wrong", + useCardano: "useCardano must be used within a CardanoProvider", + tryingConnectTo: + "You are trying to connect with a wallet connected to {{networkFrom}}. Please adjust your wallet settings to connect to {{networkTo}} or select a different wallet.", + walletNoCIP30Nor90Support: + "Your wallet does not support the required CIP-30 extension, CIP-95.", + walletNoCIP30Support: "Your wallet does not support CIP-30 extensions.", + walletNoCIP90FunctionsEnabled: + "Your wallet did not enable the needed CIP-95 functions during connection.", + }, + footer: { + copyright: "© 2023 Voltaire Gov Tool", + privacyPolicy: "Privacy policy", + }, + forms: { + hashPlaceholder: "The hash of your URL", + howCreateUrlAndHash: "How to create URL and hash?", + urlWithContextPlaceholder: "Your URL with with your context", + urlWithInfoPlaceholder: "Your URL with extra info about you", + errors: { + hashInvalidFormat: "Invalid hash format", + hashInvalidLength: "Hash must be exactly 64 characters long", + urlTooLong: "Url must be less than 65 characters", + urlInvalidFormat: "Invalid URL format", + }, + }, + govActions: { + changeVote: "Change vote", + changeYourVote: "Change your vote", + chooseHowToVote: "Choose how you want to vote:", + details: "Governance Details:", + expiryDate: "Expiry date:", + filterTitle: "Governance Action Type", + forGovAction: "for this Governance Action", + governanceActionId: "Governance Action ID:", + governanceActionType: "Governance Action Type:", + myVote: "My Vote:", + noResultsForTheSearch: "No results for the search.", + optional: "(optional)", + provideContext: "Provide context about your vote", + selectDifferentOption: "Select a different option to change your vote", + showVotes: "Show votes", + submissionDate: "Submission date:", + title: "Governance Actions", + toVote: "To vote", + viewOtherDetails: "View other details", + viewProposalDetails: "View proposal details", + vote: "Vote", + voted: "Voted", + voteOnGovActions: "Vote on Governance Action", + voteSubmitted: "Vote submitted", + voteTransaction: "Vote transaction", + votes: "Votes:", + votesSubmitted: "Votes submitted", + votesSubmittedOnChain: + "Votes submitted on-chain by DReps, SPOs and Constitutional Committee members.", + youHaventVotedYet: + "You haven't voted on any Governance Actions yet. Check the 'To vote on' section to vote on Governance Actions.", + withCategoryNotExist: { + partOne: "Governnance actions with category", + optional: "and search phrase", + partTwo: "don't exist.", + }, + withIdNotExist: { + partOne: "Governance action with id", + partTwo: "does not exist.", + }, + }, + hero: { + connectWallet: "Connect your wallet", + description: + "Interact with SanchoNet using GovTool - a friendly user\ninterface connected to SanchoNet. You can delegate\nyour voting power (tAda) or become a SanchoNet DRep\nto allow people to delegate voting power to you.", + headline: "SanchoNet \n Governance Tool", + }, + menu: { + faqs: "FAQs", + guides: "Guides", + help: "Help", + myDashboard: "My Dashboard", + viewGovActions: "View Governance Actions", + }, + metadataUpdate: { + description: + "You can include extra information about yourself by adding a URL and its hash.", + info: "Update Information", + title: "Update DRep Metadata", + }, + modals: { + common: { + goToDashboard: "Go to Dashboard", + oops: "Oops!", + }, + delegation: { + message: + "The confirmation of your actual delegation might take a bit of time but you can track it using", + title: "Delegation Transaction Submitted!", + }, + externalLink: { + beCareful: "Be Careful!", + continueTo: "Continue to external link", + description: + "Exercise caution and verify the website's authenticity before sharing personal information. To proceed, click 'Continue'. To stay on Voltaire, click 'Cancel'.", + safety: "External Link Safety", + thisIs: "This is an external link:", + youAreAboutToOpen: "You are about to open an external link to:", + }, + registration: { + message: + "The confirmation of your registration might take a bit of time but you can track it using", + title: "Registration Transaction Submitted!", + }, + retirement: { + message: + "The confirmation of your retirement might take a bit of time but you can track it using", + title: "Retirement Transaction Submitted!", + }, + votingPower: { + govActionsVotes: "Governance Action votes", + votesSubmittedByDReps: "Votes submitted by DReps", + yourVote: "Your vote", + }, + waitForTransaction: { + title: "Please wait for your previous transaction to be completed.", + message: + "Before performing a new action please wait for the previous action transaction to be completed.", + }, + }, + registration: { + descriptionStepOne: + "You can include extra information about yourself by adding a URL and its hash.", + descriptionStepTwo: + "By clicking register you create your DRep ID within your wallet and become a DRep.\n\nOnce the registration has completed your DRep ID will be shown on your dashboard. You will be able to share your DRep ID so that other ada holders can delegate their voting power to you.", + headingStepOne: "Add Information", + headingStepTwo: "Confirm DRep registration", + optional: "OPTIONAL", + register: "Register", + registerAsDRep: "Register as a DRep", + }, + slider: { + showAll: "Show all", + viewAll: "View all", + }, + system: { + sanchoNet: "SanchoNet", + sanchoNetIsBeta: + "The SanchoNet GovTool is currently in beta and it connects to ", + testAdaNote: + "Please note, this tool uses ‘Test ada’ <0>NOT real ada. All governance actions and related terms pertain to SanchoNet.", + toolConnectedToSanchonet: "This tool is connected to SanchoNet", + }, + tooltips: { + delegateTodRep: { + abstain: { + heading: "Abstaining", + paragraphOne: + "Select this to signal no confidence in the current constitutional committee by voting NO on every proposal and voting YES to no-confidence proposals.", + }, + noConfidence: { + heading: "No confidence", + paragraphOne: + "If you don’t have trust in the current constitutional committee you signal ‘No-confidence’. By voting ‘No’ means you don’t want governance actions to be ratified.", + }, + todRep: { + heading: "Delegation to DRep", + paragraphOne: + "DReps are representatives of the ada holders that can vote on governance actions.", + }, + toMyself: { + heading: "Delegate to myself", + paragraphOne: + "If you are registered as DRep you can delegate your voting power on yourself.", + }, + }, + expiryDate: { + heading: "Expiry Date", + paragraphOne: + "The date when the governance action will expiry if it doesn’t reach ratification thresholds.", + paragraphTwo: + "IMPORTANT: If the governance action is ratified before the expiry date it will be considered ratified and it will not be available to vote on afterwards.", + }, + submissionDate: { + heading: "Submission Date", + paragraphOne: + "The date when the governance action was submitted on-chain.", + }, + votingPower: { + heading: "DRep Voting Power", + paragraphOne: + "This is the voting power delegated to you as a DRep and it is calculated at the end of every epoch for the epoch that just ended.", + paragraphTwo: + "IMPORTANT: When voting, the voting power provides an indication and not the exact number.", + }, + }, + wallet: { + cantSeeWalletQuestion: + "Can’t see your wallet? Check what wallets are currently compatible with GovTool ", + chooseWallet: "Choose the wallet you want to connect with:", + connect: "Connect", + connectWallet: "Connect wallet", + connectYourWallet: "Connect your Wallet", + connectYourWalletButton: "Connect your wallet", + connectedWallet: "Connected Wallet:", + disconnect: "Disconnect", + noWalletsToConnect: + "You don't have wallets to connect, install a wallet and refresh the page and try again", + pickStakeKey: "Pick Stake Key", + selectStakeKey: "Select the stake key you want to use:", + }, + warnings: { + usingUnregisteredStakeKeys: + "Warning, no registered stake keys, using unregistered stake keys", + }, + abstain: "Abstain", + back: "Back", + backToList: "Back to the list", + cancel: "Cancel", + clear: "Clear", + confirm: "Confirm", + continue: "Continue", + delegate: "Delegate", + here: "here", + inProgress: "In progress", + learnMore: "Learn more", + loading: "Loading...", + myDRepId: "My DRep ID:", + nextStep: "Next step", + no: "No", + ok: "Ok", + select: "Select", + seeTransaction: "See transaction", + skip: "Skip", + sortBy: "Sort by", + thisLink: "this link", + votingPower: "Voting power:", + yes: "Yes", + }, +}; diff --git a/govtool/frontend/src/main.tsx b/govtool/frontend/src/main.tsx index 206c03e80..6997c7a12 100644 --- a/govtool/frontend/src/main.tsx +++ b/govtool/frontend/src/main.tsx @@ -1,9 +1,5 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import App from "./App.tsx"; -import { ThemeProvider } from "@emotion/react"; -import { ContextProviders } from "@context"; -import { theme } from "./theme.ts"; import { BrowserRouter, createRoutesFromChildren, @@ -12,8 +8,15 @@ import { useNavigationType, } from "react-router-dom"; import { QueryClient, QueryClientProvider } from "react-query"; -import * as Sentry from "@sentry/react"; import TagManager from "react-gtm-module"; +import { ThemeProvider } from "@emotion/react"; +import * as Sentry from "@sentry/react"; + +import { ContextProviders } from "@context"; + +import App from "./App.tsx"; +import { theme } from "./theme.ts"; +import "./i18n"; const queryClient = new QueryClient(); diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx index c6b27dadc..a073476e1 100644 --- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx @@ -23,6 +23,7 @@ import { useFetchNextPageDetector, useSaveScrollPosition, useScreenDimension, + useTranslation, } from "@hooks"; import { getFullGovActionId, @@ -39,6 +40,7 @@ export const DashboardGovernanceActionsCategory = ({}) => { const { isMobile, screenWidth } = useScreenDimension(); const navigate = useNavigate(); const { dRep, voteTransaction } = useCardano(); + const { t } = useTranslation(); const { data: dRepVotes } = useGetDRepVotesQuery([], ""); @@ -70,7 +72,7 @@ export const DashboardGovernanceActionsCategory = ({}) => { style={{ textDecorationColor: "#0033AD" }} > - Governance Actions + {t("govActions.title")} , @@ -149,7 +151,7 @@ export const DashboardGovernanceActionsCategory = ({}) => { style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - Back to the list + {t("backToList")} { - Governnance actions with category  + {t("govActions.withCategoryNotExist.partOne")}  {` ${category} `} -  don't exist. + +  {t("govActions.withCategoryNotExist.partTwo")} + ) : ( diff --git a/govtool/frontend/src/pages/DelegateTodRep.tsx b/govtool/frontend/src/pages/DelegateTodRep.tsx index 1c2c20332..8749bb225 100644 --- a/govtool/frontend/src/pages/DelegateTodRep.tsx +++ b/govtool/frontend/src/pages/DelegateTodRep.tsx @@ -9,7 +9,7 @@ import { DelegateTodRepStepTwo, Footer, } from "@organisms"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { WALLET_LS_KEY, getItemFromLocalStorage } from "@/utils/localStorage"; import { useNavigate } from "react-router-dom"; @@ -17,6 +17,7 @@ export const DelegateTodRep = () => { const [step, setStep] = useState(1); const { isMobile } = useScreenDimension(); const navigate = useNavigate(); + const { t } = useTranslation(); useEffect(() => { if ( @@ -34,7 +35,7 @@ export const DelegateTodRep = () => { imageSRC={ICONS.appLogoIcon} imageWidth={isMobile ? undefined : 42} imageHeight={isMobile ? 24 : 35} - title={"Delegate to DRep"} + title={t("delegation.toDRep.title")} /> @@ -40,19 +41,22 @@ export const ErrorPage = ({ lineHeight={"64px"} sx={{ whiteSpace: "nowrap" }} > - Whoops! + {t("errorPage.whoops")} {state && state.errorCode === 500 - ? "We have an internal server error." + ? t("errorPage.serverError") : errorDescription} - Error {state ? state.errorCode : errorCode} + {t("errorPage.error")} + {state ? state.errorCode : errorCode} {isButton && ( )}
diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index f3be06b8b..76c26e93b 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -11,7 +11,11 @@ import { Box, Breadcrumbs, CircularProgress, Link } from "@mui/material"; import { Background, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; import { useCardano } from "@context"; -import { useGetProposalQuery, useScreenDimension } from "@hooks"; +import { + useGetProposalQuery, + useScreenDimension, + useTranslation, +} from "@hooks"; import { Footer, TopNav, GovernanceActionDetailsCard } from "@organisms"; import { formatDisplayDate, @@ -26,6 +30,7 @@ export const GovernanceActionDetails = () => { const navigate = useNavigate(); const { pagePadding, screenWidth } = useScreenDimension(); const { isEnabled } = useCardano(); + const { t } = useTranslation(); const { proposalId } = useParams(); const fullProposalId = proposalId + hash; @@ -50,11 +55,11 @@ export const GovernanceActionDetails = () => { style={{ textDecorationColor: "#0033AD" }} > - Governance Actions + {t("govActions.title")} , - Vote on Governance Action + {t("govActions.voteOnGovActions")} , ]; @@ -77,7 +82,7 @@ export const GovernanceActionDetails = () => { {screenWidth >= 1024 ? ( - Governance Actions + {t("govActions.title")} ) : null} { style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - Back to the list + {t("backToList")} {isLoading ? ( @@ -159,12 +164,14 @@ export const GovernanceActionDetails = () => { ) : ( - Governnance action with id  + {t("govActions.withIdNotExist.partOne")}  {` ${shortenedGovActionId} `} -  does not exist. + +  {t("govActions.withIdNotExist.partTwo")} + )} diff --git a/govtool/frontend/src/pages/GovernanceActions.tsx b/govtool/frontend/src/pages/GovernanceActions.tsx index 0cc16b470..ee4c2f561 100644 --- a/govtool/frontend/src/pages/GovernanceActions.tsx +++ b/govtool/frontend/src/pages/GovernanceActions.tsx @@ -5,7 +5,7 @@ import { Box, Divider } from "@mui/material"; import { Background, ScrollToManage, Typography } from "@atoms"; import { PATHS } from "@consts"; import { useCardano } from "@context"; -import { useScreenDimension } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { DataActionsBar } from "@molecules"; import { Footer, TopNav, GovernanceActionsToVote } from "@organisms"; import { WALLET_LS_KEY, getItemFromLocalStorage } from "@utils"; @@ -19,6 +19,7 @@ export const GovernanceActions = () => { const { isMobile, pagePadding } = useScreenDimension(); const { isEnabled } = useCardano(); const navigate = useNavigate(); + const { t } = useTranslation(); useEffect(() => { if (isEnabled && getItemFromLocalStorage(`${WALLET_LS_KEY}_stake_key`)) { @@ -49,7 +50,7 @@ export const GovernanceActions = () => { sx={{ mb: isMobile ? 3.75 : 6, px: pagePadding }} variant={isMobile ? "title1" : "headline3"} > - Governance Actions + {t("govActions.title")} {isMobile && ( { const { isEnabled } = useCardano(); const navigate = useNavigate(); const { dRep } = useCardano(); + const { t } = useTranslation(); const { data: dRepVotes, @@ -72,7 +74,7 @@ export const GovernanceActionsCategory = ({}) => { style={{ textDecorationColor: "#0033AD" }} > - Governance Actions + {t("govActions.title")} , @@ -121,7 +123,7 @@ export const GovernanceActionsCategory = ({}) => { sx={{ mb: isMobile ? 3.75 : 6, px: pagePadding }} variant={isMobile ? "title1" : "headline3"} > - Governance Actions + {t("govActions.title")} {isMobile && ( { style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - Back to the list + {t("backToList")} { - Governnance actions with category  + {t("govActions.withCategoryNotExist.partOne")}  {category}  {searchText && ( <> - and search phrase  + {t("govActions.withCategoryNotExist.optional")}  {searchText} )} -  don't exist. + +  {t("govActions.withCategoryNotExist.partTwo")} + ) : ( diff --git a/govtool/frontend/src/pages/RegisterAsdRep.tsx b/govtool/frontend/src/pages/RegisterAsdRep.tsx index 8f4e31d93..79e3b1c88 100644 --- a/govtool/frontend/src/pages/RegisterAsdRep.tsx +++ b/govtool/frontend/src/pages/RegisterAsdRep.tsx @@ -13,6 +13,7 @@ import { import { useScreenDimension, useUrlAndHashFormController as useRegisterAsdRepFormController, + useTranslation, } from "@hooks"; import { useNavigate } from "react-router-dom"; import { WALLET_LS_KEY, getItemFromLocalStorage } from "@/utils/localStorage"; @@ -21,6 +22,7 @@ export const RegisterAsdRep = () => { const [step, setStep] = useState(1); const { isMobile } = useScreenDimension(); const navigate = useNavigate(); + const { t } = useTranslation(); const registerAsdRepFormMethods = useRegisterAsdRepFormController(); @@ -40,7 +42,7 @@ export const RegisterAsdRep = () => { imageSRC={ICONS.appLogoIcon} imageWidth={isMobile ? undefined : 42} imageHeight={isMobile ? 24 : 35} - title={"Register as a DRep"} + title={t("registration.registerAsDRep")} /> { } = theme; const { isMobile, pagePadding, screenWidth } = useScreenDimension(); const { isPendingTransaction } = useCardano(); + const { t } = useTranslation(); const { submitForm, control, errors, isValid, isLoading } = useUpdatedRepMetadataForm(); @@ -45,7 +50,7 @@ export const UpdatedRepMetadata = () => { }} variant="outlined" > - Cancel + {t("cancel")} ); }, [isMobile]); @@ -66,7 +71,7 @@ export const UpdatedRepMetadata = () => { }} variant="contained" > - Confirm + {t("confirm")} ); }, [isLoading, isMobile, isValid, submitForm]); @@ -78,7 +83,7 @@ export const UpdatedRepMetadata = () => { imageSRC={ICONS.appLogoIcon} imageWidth={isMobile ? undefined : 42} imageHeight={isMobile ? 24 : 35} - title={"Update DRep Metadata"} + title={t("metadataUpdate.title")} /> { sx={{ mt: 1, textAlign: "center" }} variant="headline4" > - Update Information + {t("metadataUpdate.info")} - You can include extra information about yourself by adding a URL - and its hash. + {t("metadataUpdate.description")} { { sx={{ cursor: "pointer" }} > - How to create URL and hash? + {t("forms.howCreateUrlAndHash")} diff --git a/govtool/frontend/src/types/i18next.d.ts b/govtool/frontend/src/types/i18next.d.ts new file mode 100644 index 000000000..a6063604e --- /dev/null +++ b/govtool/frontend/src/types/i18next.d.ts @@ -0,0 +1,11 @@ +import { en } from "@/i18n/locales/en"; + +declare module "i18next" { + interface CustomTypeOptions { + defaultNS: "en"; + + resources: { + en: (typeof en)["translation"]; + }; + } +} diff --git a/govtool/frontend/yarn.lock b/govtool/frontend/yarn.lock index a44fbdbb1..b10b62e26 100644 --- a/govtool/frontend/yarn.lock +++ b/govtool/frontend/yarn.lock @@ -1016,6 +1016,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -3850,37 +3857,37 @@ "@types/babel__core" "^7.20.5" react-refresh "^0.14.0" -"@vitest/expect@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.1.0.tgz#f58eef7de090ad65f30bb93ec54fa9f94c9d1d5d" - integrity sha512-9IE2WWkcJo2BR9eqtY5MIo3TPmS50Pnwpm66A6neb2hvk/QSLfPXBz2qdiwUOQkwyFuuXEUj5380CbwfzW4+/w== +"@vitest/expect@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.2.2.tgz#39ea22e849bbf404b7e5272786551aa99e2663d0" + integrity sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg== dependencies: - "@vitest/spy" "1.1.0" - "@vitest/utils" "1.1.0" + "@vitest/spy" "1.2.2" + "@vitest/utils" "1.2.2" chai "^4.3.10" -"@vitest/runner@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.1.0.tgz#b3bf60f4a78f4324ca09811dd0f87b721a96b534" - integrity sha512-zdNLJ00pm5z/uhbWF6aeIJCGMSyTyWImy3Fcp9piRGvueERFlQFbUwCpzVce79OLm2UHk9iwaMSOaU9jVHgNVw== +"@vitest/runner@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.2.2.tgz#8b060a56ecf8b3d607b044d79f5f50d3cd9fee2f" + integrity sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg== dependencies: - "@vitest/utils" "1.1.0" + "@vitest/utils" "1.2.2" p-limit "^5.0.0" pathe "^1.1.1" -"@vitest/snapshot@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.1.0.tgz#b9924e4303382b43bb2c31061b173e69a6fb3437" - integrity sha512-5O/wyZg09V5qmNmAlUgCBqflvn2ylgsWJRRuPrnHEfDNT6tQpQ8O1isNGgo+VxofISHqz961SG3iVvt3SPK/QQ== +"@vitest/snapshot@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.2.2.tgz#f56fd575569774968f3eeba9382a166c26201042" + integrity sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA== dependencies: magic-string "^0.30.5" pathe "^1.1.1" pretty-format "^29.7.0" -"@vitest/spy@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.1.0.tgz#7f40697e4fc217ac8c3cc89a865d1751b263f561" - integrity sha512-sNOVSU/GE+7+P76qYo+VXdXhXffzWZcYIPQfmkiRxaNCSPiLANvQx5Mx6ZURJ/ndtEkUJEpvKLXqAYTKEY+lTg== +"@vitest/spy@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.2.2.tgz#8fc2aeccb96cecbbdd192c643729bd5f97a01c86" + integrity sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g== dependencies: tinyspy "^2.2.0" @@ -3906,6 +3913,16 @@ loupe "^2.3.7" pretty-format "^29.7.0" +"@vitest/utils@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.2.2.tgz#94b5a1bd8745ac28cf220a99a8719efea1bcfc83" + integrity sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g== + dependencies: + diff-sequences "^29.6.3" + estree-walker "^3.0.3" + loupe "^2.3.7" + pretty-format "^29.7.0" + "@yarnpkg/esbuild-plugin-pnp@^3.0.0-rc.10": version "3.0.0-rc.15" resolved "https://registry.yarnpkg.com/@yarnpkg/esbuild-plugin-pnp/-/esbuild-plugin-pnp-3.0.0-rc.15.tgz#4e40e7d2eb28825c9a35ab9d04c363931d7c0e67" @@ -3947,10 +3964,10 @@ acorn-walk@^7.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.3.0: - version "8.3.1" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.1.tgz#2f10f5b69329d90ae18c58bf1fa8fccd8b959a43" - integrity sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw== +acorn-walk@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== acorn@^7.4.1: version "7.4.1" @@ -5557,6 +5574,13 @@ estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -6267,6 +6291,13 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + html-tags@^3.1.0: version "3.3.1" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" @@ -6329,6 +6360,13 @@ human-signals@^5.0.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== +i18next@^23.7.19: + version "23.7.19" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.7.19.tgz#037ac683610417178b881355e5cdc38380c0ca17" + integrity sha512-1aP+YSJl+nLxr42ZJtNhpWpNWYsc6nCbVCf2x4uizIX1BYfcigiRMlb3vOkE1p3+qrI+aD6h5G2Fg+2d2oMIOQ== + dependencies: + "@babel/runtime" "^7.23.2" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -8602,6 +8640,14 @@ react-hook-form@^7.47.0: resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.48.2.tgz#01150354d2be61412ff56a030b62a119283b9935" integrity sha512-H0T2InFQb1hX7qKtDIZmvpU1Xfn/bdahWBN1fH19gSe4bBEqTfmlr7H3XWTaVtiK4/tpPaI1F3355GPMZYge+A== +react-i18next@^14.0.1: + version "14.0.1" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-14.0.1.tgz#75351b25be076ad7391360b5a111b59ca87e0b63" + integrity sha512-TMV8hFismBmpMdIehoFHin/okfvgjFhp723RYgIqB4XyhDobVMyukyM3Z8wtTRmajyFMZrBl/OaaXF2P6WjUAw== + dependencies: + "@babel/runtime" "^7.22.5" + html-parse-stringify "^3.0.1" + react-is@18.1.0: version "18.1.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" @@ -9577,10 +9623,10 @@ tinybench@^2.5.1: resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.1.tgz#3408f6552125e53a5a48adee31261686fd71587e" integrity sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg== -tinypool@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.1.tgz#b6c4e4972ede3e3e5cda74a3da1679303d386b03" - integrity sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg== +tinypool@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.2.tgz#84013b03dc69dacb322563a475d4c0a9be00f82a" + integrity sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ== tinyspy@^2.2.0: version "2.2.0" @@ -9953,10 +9999,10 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -vite-node@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.1.0.tgz#0ebcb7398692e378954786dfba28e905e28a76b4" - integrity sha512-jV48DDUxGLEBdHCQvxL1mEh7+naVy+nhUUUaPAZLd3FJgXuxQiewHcfeZebbJ6onDqNGkP4r3MhQ342PRlG81Q== +vite-node@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.2.2.tgz#f6d329b06f9032130ae6eac1dc773f3663903c25" + integrity sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg== dependencies: cac "^6.7.14" debug "^4.3.4" @@ -9986,9 +10032,9 @@ vite@^4.3.9: fsevents "~2.3.2" vite@^5.0.0: - version "5.0.10" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.10.tgz#1e13ef5c3cf5aa4eed81f5df6d107b3c3f1f6356" - integrity sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw== + version "5.0.12" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.12.tgz#8a2ffd4da36c132aec4adafe05d7adde38333c47" + integrity sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w== dependencies: esbuild "^0.19.3" postcss "^8.4.32" @@ -9997,16 +10043,16 @@ vite@^5.0.0: fsevents "~2.3.3" vitest@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.1.0.tgz#47ba67c564aa137b53b0197d2a992908e7f5b04d" - integrity sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A== - dependencies: - "@vitest/expect" "1.1.0" - "@vitest/runner" "1.1.0" - "@vitest/snapshot" "1.1.0" - "@vitest/spy" "1.1.0" - "@vitest/utils" "1.1.0" - acorn-walk "^8.3.0" + version "1.2.2" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.2.2.tgz#9e29ad2a74a5df553c30c5798c57a062d58ce299" + integrity sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw== + dependencies: + "@vitest/expect" "1.2.2" + "@vitest/runner" "1.2.2" + "@vitest/snapshot" "1.2.2" + "@vitest/spy" "1.2.2" + "@vitest/utils" "1.2.2" + acorn-walk "^8.3.2" cac "^6.7.14" chai "^4.3.10" debug "^4.3.4" @@ -10018,11 +10064,16 @@ vitest@^1.1.0: std-env "^3.5.0" strip-literal "^1.3.0" tinybench "^2.5.1" - tinypool "^0.8.1" + tinypool "^0.8.2" vite "^5.0.0" - vite-node "1.1.0" + vite-node "1.2.2" why-is-node-running "^2.2.2" +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + w3c-xmlserializer@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c"