diff --git a/next-i18next.config.js b/next-i18next.config.js new file mode 100644 index 0000000..e7337c5 --- /dev/null +++ b/next-i18next.config.js @@ -0,0 +1,13 @@ +const path = require('path') +const intervalPlural = require('i18next-intervalplural-postprocessor') + +module.exports = { + i18n: { + defaultLocale: 'en', + locales: ['en', 'de'], + use: [intervalPlural], + serializeConfig: false + }, + localePath: path.resolve('./public/locales'), + reloadOnPrerender: process.env.NODE_ENV === 'development' +} diff --git a/next.config.js b/next.config.js index 8663ef4..467a6bd 100644 --- a/next.config.js +++ b/next.config.js @@ -1,3 +1,5 @@ +import { i18n } from './next-i18next.config' + // eslint-disable-next-line @typescript-eslint/no-var-requires const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true' @@ -25,7 +27,7 @@ module.exports = (_phase, { defaultConfig }) => { ...nextConfig }) - const finalConfig = {} + const finalConfig = { i18n } Object.keys(wConfig).forEach((key) => { if (!KEYS_TO_OMIT.includes(key)) { finalConfig[key] = wConfig[key] diff --git a/package.json b/package.json index b38a168..871c64f 100644 --- a/package.json +++ b/package.json @@ -50,10 +50,14 @@ "@rainbow-me/rainbowkit": "^1.0.11", "@types/next-pwa": "^5.6.4", "chakra-react-select": "^4.7.6", + "i18next": "^23.7.14", + "i18next-intervalplural-postprocessor": "^3.0.0", "next": "13.2.4", + "next-i18next": "^15.1.2", "next-pwa": "^5.6.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^14.0.0", "react-icons": "^4.11.0", "react-merge-refs": "^2.0.1", "styled-components": "^6.0.0", diff --git a/public/locales/en/common.json b/public/locales/en/common.json new file mode 100644 index 0000000..8d77fba --- /dev/null +++ b/public/locales/en/common.json @@ -0,0 +1,228 @@ +{ + "404": { + "header": "404 - Something went wrong" + }, + "500": { + "header": "500 - Something went wrong" + }, + "index": { + "setupNode": { + "header": "Setup a Node", + "linkText": "Please follow the docs to setup a node", + "alt": "Setup a Node Readme" + } + }, + "manage": { + "header": "Manage the stake" + }, + "nominators": { + "header": "Nominators" + }, + "operators": { + "header": "Operators" + }, + "register": { + "header": "Register as operator", + "form": { + "domainId": { + "label": "Domain ID", + "placeholder": "Enter the domain ID", + "error": "The Domain ID you enter is not valid" + }, + "amountToStake": { + "label": "Amount to stake, {{tokenSymbol}}", + "placeholder": "Enter the amount to stake", + "error": "The amount to stake you enter is not valid" + }, + "signingKey": { + "label": "Signing Key", + "placeholder": "Enter the signing key", + "error": "The signing key you enter is not valid" + }, + "minimumNominatorStake": { + "label": "Minimum Nominator Stake, {{tokenSymbol}}", + "placeholder": "Enter the minimum nominator stake", + "error": "The minimum nominator stake you enter is not valid" + }, + "nominatorTax": { + "label": "Nomination Tax, %", + "placeholder": "Enter the nominator tax", + "error": "The nominator tax you enter is not valid" + } + }, + "noRow": "No operators found", + "needHelpLink": "Need help? Check the docs" + }, + "stats": { + "header": "Stats" + }, + "components": { + "actions": { + "header": "Action", + "operatorHeader": "{{actionSelected}} on Operator Id #{operatorId}", + "operatorAccount": "Operator Account", + "signingKey": "Signing Key", + "confirmDeRegistration": "Do you really want to de-register as operator?", + "field": { + "amount": { + "placeholder": "Amount, {{tokenSymbol}}", + "error": "The amount you enter is not valid<" + } + } + }, + "buttons": { + "connectWallet": "Connect Wallet" + }, + "fundsInStake": { + "header": "Funds in Stake", + "operatorStake": "Operator stake", + "nominatorsStake": "Nominators stake", + "totalStake": "Total stake" + }, + "intro": { + "header": "Staking as a pool operator", + "paragraphOne": "{{tokenSymbol}} holders (Gemini 3g testnet network only) can stake their {{tokenSymbol}} to add more security to the protocol and earn", + "paragraphTwo": "Currently Staking Wars is active, please read this", + "paragraphTree": "on how to participate and earn even more rewards!", + "stakingIncentives": "Staking Incentives", + "information": "information", + "learnMore": "Learn more about risks involved." + }, + "layout": { + "menu": { + "stake": "Stake as a pool operator", + "manage": "Manage your stake", + "stats": "Stats" + }, + "footer": { + "otherTools": "Other tools", + "social": "Social", + "links": { + "operatorDocs": "Operator documentation", + "astralExplorer": "Astral Explorer" + }, + "socials": { + "discord": "Discord", + "telegram": "Telegram", + "twitter": "Twitter", + "github": "GitHub", + "reddit": "Reddit", + "medium": "Medium", + "youtube": "Youtube", + "linkedin": "Linkedin" + } + } + }, + "nominatorsList": { + "header": "Information across nominators", + "headerOperatorId": "on Operator ID {operatorId}", + "nominatorAccount": "Nominator Account", + "operatorID": "OperatorID", + "nominatorTax": "Nominator Tax", + "minNominatorsStake": "Min Nominator Stake", + "fundsInStake": "Funds in stake", + "pcOfStake": "% of Stake", + "totalFundsStaked": "Total Funds Staked", + "noRow": "No nominators found", + "operator": "Operator" + }, + "operatorsCards": { + "header": "Information across operators", + "headerOperatorOwner": "on Account {account}", + "operatorId": "operatorID #{operatorId}", + "nominators": "{nominatorsCount} Nominators", + "nominatorTax": "Nominator Tax", + "minNominatorsStake": "Min Nominator Stake", + "fundsInStake": "Funds in stake", + "noRow": "No operators found", + "delayWarning": "If you recently register a operator, it may take up to 10 minutes for the operator to be added." + }, + "operatorsList": { + "header": "Information across operators", + "headerOperatorOwner": "on Account {account}", + "table": { + "operatorId": "OperatorID", + "signingKey": "Signing key", + "operatorAccount": "Operator Account", + "nominatorsCount": "Nominators Count", + "nominatorTax": "Nominator Tax", + "minNominatorStake": "Min Nominator Stake", + "fundsInStake": "Funds in stake" + }, + "delayWarning": "If you recently register a operator, it may take up to 10 minutes for the operator to be added." + }, + "operatorsTotal": { + "header": "Aggregated data", + "headerOperatorOwner": "on Account {{account}}", + "fundsInStake": "Funds in Stake, {{tokenSymbol}}", + "operatorsCount": "Number of Operators", + "nominatorsCount": "Number of Nominators", + "availableForWithdrawal": "Available for Withdrawal, {tokenSymbol}", + "operatorsFunds": "Operators Funds, {tokenSymbol}", + "nominatorsFunds": "Nominators Funds, {tokenSymbol}" + }, + "pieGraph": { + "totalStake": "Total stake", + "quantity": "Quantity", + "nominators": "nominators", + "operators": "operators" + }, + "toasts": { + "viewInExplorer": "View on Subscan Explorer" + }, + "transactionsSpotter": { + "noTxYet": "No transactions yet" + }, + "viewSelector": { + "listView": "List view", + "gridView": "Grid view", + "orderBy": "Order by", + "switchOrder": "Switch order" + }, + "wallet": { + "polkadot": "Polkadot.js", + "metamask": "MetaMask Snap", + "subwallet": "SubWallet", + "connectWallet": "Connect Wallet", + "connectYourWallet": "Connect your wallet", + "selectWallet": "Select your wallet provider", + "disconnect": "Disconnect" + }, + "walletDetails": { + "tokenBalanceLabel": "Account balance", + "tokenStakedLabel": "Account balance staked or nominated" + } + }, + "action": { + "actions": "Actions", + "next": "Next", + "max": "Max", + "close": "Close", + "submit": "Submit", + "pleaseConnectWallet": "Please connect your wallet" + }, + "infos": { + "registrationSend": { + "title": "Registration request sent", + "description": "Your registration tx. was sent. The transaction need to be minted then, you will see the change after the next epoch." + }, + "deregisterOperatorSend": { + "title": "De-registration request sent", + "description": "Your request to de-register tx. was sent. The transaction need to be minted then, you will see the change after the next epoch." + }, + "nominateOperatorSend": { + "title": "Add funds request sent", + "description": "Your request to add funds tx. was sent. The transaction need to be minted then, you will see the change after the next epoch." + }, + "withdrawStakeSend": { + "title": "Withdraw request sent", + "description": "Your withdraw request tx. was sent. The transaction need to be minted then, you will see the change after the next epoch." + } + }, + "warnings": { + "multipleWalletsDetected": { + "title": "Multiple wallet extensions detected", + "description": "In some cases, having multiple wallet extensions enabled at the same time can cause issues." + } + } +} diff --git a/src/components/actions.tsx b/src/components/actions.tsx index a077049..6c94c80 100644 --- a/src/components/actions.tsx +++ b/src/components/actions.tsx @@ -22,6 +22,7 @@ import { VStack, useDisclosure } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' import Link from 'next/link' import { useCallback, useMemo, useRef, useState } from 'react' import { ActionType, ROUTES, actionButtonStyles } from '../constants' @@ -35,6 +36,7 @@ interface ActionsProps { } export const Actions: React.FC = ({ operatorId }) => { + const { t } = useTranslation() const finalRef = useRef(null) const { isOpen, onOpen, onClose } = useDisclosure() const { subspaceAccount, stakingConstants, chainDetails } = useExtension() @@ -89,7 +91,7 @@ export const Actions: React.FC = ({ operatorId }) => { <> }> - Action + {t('components.actions.header')} {ActionsList.map((action) => ( @@ -111,14 +113,18 @@ export const Actions: React.FC = ({ operatorId }) => { - {actionSelected != null && capitalizeFirstLetter(actionSelected)} on Operator Id #{operatorId} + {actionSelected != null && + t('components.actions.operatorHeader', { + actionSelected: capitalizeFirstLetter(actionSelected), + operatorId + })} - Operator Account + {t('components.actions.operatorAccount')} {operator && operator.operatorOwner} - Signing Key + {t('components.actions.signingKey')} {operator && ( @@ -126,7 +132,7 @@ export const Actions: React.FC = ({ operatorId }) => { )} - {actionSelected === ActionType.Deregister && Do you really want to de-register as operator?} + {actionSelected === ActionType.Deregister && {t('components.actions.confirmDeRegistration')}} {actionSelected === ActionType.AddFunds && ( @@ -134,19 +140,19 @@ export const Actions: React.FC = ({ operatorId }) => { name='amount' borderColor='brand.500' border='1px' - placeholder={`Amount, ${tokenSymbol}`} + placeholder={t('components.actions.field.amount.placeholder', { tokenSymbol })} value={addFundsAmount.formattedAmount} onChange={(e) => handleChangeAmount(ActionType.AddFunds, e)} _placeholder={{ color: '#7D7D7D' }} /> {isErrorsField[ActionType.AddFunds] ? ( - The amount you enter is not valid + {t('components.actions.field.amount.error')} ) : ( )} @@ -159,19 +165,19 @@ export const Actions: React.FC = ({ operatorId }) => { name='amount' borderColor='brand.500' border='1px' - placeholder={`Amount, ${tokenSymbol}`} + placeholder={t('components.actions.field.amount.placeholder', { tokenSymbol })} value={withdrawAmount.formattedAmount} onChange={(e) => handleChangeAmount(ActionType.Withdraw, e)} _placeholder={{ color: '#7D7D7D' }} /> {isErrorsField[ActionType.Withdraw] ? ( - The amount you enter is not valid + {t('components.actions.field.amount.error')} ) : ( )} @@ -182,10 +188,10 @@ export const Actions: React.FC = ({ operatorId }) => { diff --git a/src/components/buttons.tsx b/src/components/buttons.tsx index f1aee14..55c5e0e 100644 --- a/src/components/buttons.tsx +++ b/src/components/buttons.tsx @@ -1,4 +1,5 @@ import { Button, ButtonProps } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' import dynamic from 'next/dynamic' import React, { useEffect, useState } from 'react' import { buttonStyles, connectWalletButtonStyles } from '../constants' @@ -16,6 +17,7 @@ export const FormButton: React.FC = ({ children, onClick }) => { } export const ConnectWallet: React.FC = ({ onClick }) => { + const { t } = useTranslation() const [clientSide, setClientSide] = useState(false) useEffect(() => { @@ -25,7 +27,7 @@ export const ConnectWallet: React.FC = ({ onClick }) => { if (!clientSide) return ( ) diff --git a/src/components/fundsInStake.tsx b/src/components/fundsInStake.tsx index cb18787..5886e40 100644 --- a/src/components/fundsInStake.tsx +++ b/src/components/fundsInStake.tsx @@ -13,6 +13,7 @@ import { Portal, Text } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' import React, { useMemo } from 'react' import { textStyles } from '../constants' import { useExtension } from '../states/extension' @@ -24,6 +25,7 @@ interface ActionsProps { } export const FundsInStake: React.FC = ({ operatorId }) => { + const { t } = useTranslation() const { chainDetails, stakingConstants } = useExtension() const operator = useMemo( @@ -72,17 +74,17 @@ export const FundsInStake: React.FC = ({ operatorId }) => { - Funds in Stake + {t('components.fundsInStake.header')} - Operator stake: + {t('components.fundsInStake.operatorStake')}: {operatorStake ? formatNumber(operatorStake) : '0'} {chainDetails.tokenSymbol} - Nominators stake: + {t('components.fundsInStake.nominatorsStake')}: {nominatorsStake ? formatNumber(nominatorsStake) : '0'} {chainDetails.tokenSymbol} @@ -91,18 +93,12 @@ export const FundsInStake: React.FC = ({ operatorId }) => { - Total stake: + {t('components.fundsInStake.totalStake')}: {hexToFormattedNumber(operator.operatorDetail.currentTotalStake)} {chainDetails.tokenSymbol} - Total shares: - - - {hexToFormattedNumber(operator.operatorDetail.totalShares)} {chainDetails.tokenSymbol} - - diff --git a/src/components/intro.tsx b/src/components/intro.tsx index c90e1e1..429ea97 100644 --- a/src/components/intro.tsx +++ b/src/components/intro.tsx @@ -1,4 +1,5 @@ import { Box, HStack, Heading, Text } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' import Link from 'next/link' import React from 'react' import { EXTERNAL_ROUTES, headingStyles } from '../constants' @@ -6,6 +7,7 @@ import { useExtension } from '../states/extension' import { Wallet } from './icons' export const Intro: React.FC = () => { + const { t } = useTranslation() const { chainDetails: { tokenSymbol } } = useExtension() @@ -14,25 +16,24 @@ export const Intro: React.FC = () => { - Staking as a pool operator + {t('components.intro.header')} - {tokenSymbol} holders (Gemini 3g testnet network only) can stake their {tokenSymbol} to add more security to the - protocol and earn{' '} + {t('components.intro.paragraphOne', { tokenSymbol })}{' '} - Staking Incentives + {t('components.intro.stakingIncentives')} . - Currently Staking Wars is active, please read this{' '} + {t('components.intro.paragraphTwo')}{' '} - information + {t('components.intro.information')} {' '} - on how to participate and earn even more rewards! + {t('components.intro.paragraphTree')} - Learn more about risks involved. + {t('components.intro.learnMore')} ) diff --git a/src/components/layout.tsx b/src/components/layout.tsx index 66af943..66d8d0e 100644 --- a/src/components/layout.tsx +++ b/src/components/layout.tsx @@ -22,6 +22,7 @@ import { useDisclosure, useMediaQuery } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' import Image from 'next/image' import Link from 'next/link' import React, { useEffect, useMemo } from 'react' @@ -37,6 +38,7 @@ interface LayoutProps { } export const MobileMenu: React.FC = () => { + const { t } = useTranslation() const { isOpen, onOpen, onClose } = useDisclosure() const { setIsMobile } = useView() const [isLargerThan800] = useMediaQuery('(min-width: 800px)', { ssr: true, fallback: false }) @@ -70,7 +72,7 @@ export const MobileMenu: React.FC = () => { pt='8px' pb='7px' onClick={onClose}> - Stake as a pool operator + {t('components.layout.menu.stake')} @@ -85,7 +87,7 @@ export const MobileMenu: React.FC = () => { pt='8px' pb='7px' onClick={onClose}> - Manage your stake + {t('components.layout.menu.manage')} @@ -100,7 +102,7 @@ export const MobileMenu: React.FC = () => { pt='8px' pb='7px' onClick={onClose}> - Stats + {t('components.layout.menu.stats')} @@ -118,6 +120,7 @@ export const MobileMenu: React.FC = () => { } export const Header: React.FC = () => { + const { t } = useTranslation() const { isMobile } = useView() return isMobile ? ( @@ -136,17 +139,17 @@ export const Header: React.FC = () => { @@ -160,6 +163,7 @@ export const Header: React.FC = () => { } export const Footer: React.FC = () => { + const { t } = useTranslation() const { isMobile } = useView() const links = useMemo( @@ -169,28 +173,28 @@ export const Footer: React.FC = () => {
- Other tools + {t('components.layout.footer.otherTools')}
- Operator documentation + {t('components.layout.footer.links.operatorDocs')} - Astral Explorer + {t('components.layout.footer.links.astralExplorer')} ), - [] + [t] ) const socials = useMemo( @@ -198,63 +202,135 @@ export const Footer: React.FC = () => {
- Social + {t('components.layout.footer.social')}
- - Reddit + + {t('components.layout.footer.socials.discord')} - - Reddit + + {t('components.layout.footer.socials.telegram')} - - Reddit + + {t('components.layout.footer.socials.twitter')} - - Reddit + + {t('components.layout.footer.socials.github')} - - Reddit + + {t('components.layout.footer.socials.reddit')} - - Reddit + + {t('components.layout.footer.socials.medium')} - - Reddit + + {t('components.layout.footer.socials.youtube')} - - Reddit + + {t('components.layout.footer.socials.linkedin')} diff --git a/src/components/nominatorsList.tsx b/src/components/nominatorsList.tsx index f2d422d..bff4b22 100644 --- a/src/components/nominatorsList.tsx +++ b/src/components/nominatorsList.tsx @@ -1,5 +1,6 @@ import { Box, HStack, Heading, Table, TableContainer, Tag, Tbody, Td, Text, Th, Thead, Tr } from '@chakra-ui/react' import { encodeAddress } from '@polkadot/keyring' +import { useTranslation } from 'next-i18next' import Link from 'next/link' import React, { useEffect, useMemo, useState } from 'react' import { ROUTES, headingStyles, tHeadStyles, tableStyles, textStyles } from '../constants' @@ -14,6 +15,7 @@ interface OperatorsListProps { } export const NominatorsList: React.FC = ({ operatorId }) => { + const { t } = useTranslation() const [clientSide, setClientSide] = useState(false) const { extension, @@ -42,22 +44,26 @@ export const NominatorsList: React.FC = ({ operatorId }) => - Information across nominators - {operatorId && on Operator ID {operatorId}} + {t('components.nominatorsList.header')} + {operatorId && ( + + {t('components.nominatorsList.headerOperatorOwner', { operatorId })} + + )} - - - - - - - - {subspaceAccount && } + + + + + + + + {subspaceAccount && } {nominators.length === 0 ? ( @@ -65,7 +71,7 @@ export const NominatorsList: React.FC = ({ operatorId }) => {[0].map((_, key) => ( ))} @@ -102,7 +108,7 @@ export const NominatorsList: React.FC = ({ operatorId }) => {isOperator && ( - Operator + {t('components.nominatorsList.operator')} )} diff --git a/src/components/operatorsCards.tsx b/src/components/operatorsCards.tsx index 4760e9c..10aa46a 100644 --- a/src/components/operatorsCards.tsx +++ b/src/components/operatorsCards.tsx @@ -1,5 +1,6 @@ import { Box, Card, Grid, GridItem, HStack, Heading, Tag, Text, VStack } from '@chakra-ui/react' import { encodeAddress } from '@polkadot/keyring' +import { useTranslation } from 'next-i18next' import Link from 'next/link' import React, { useEffect, useState } from 'react' import { ROUTES, headingStyles, textStyles } from '../constants' @@ -16,6 +17,7 @@ interface operatorsCardsProps { } export const OperatorsCards: React.FC = ({ operatorOwner, fromManage }) => { + const { t } = useTranslation() const [clientSide, setClientSide] = useState(false) const { extension, @@ -35,9 +37,11 @@ export const OperatorsCards: React.FC = ({ operatorOwner, f - Information across operators + {t('components.operatorsCards.header')} {operatorOwner && ( - on Account {formatAddress(operatorOwner)} + + {t('components.operatorsCards.headerOperatorOwner', { account: formatAddress(operatorOwner) })} + )} @@ -45,11 +49,9 @@ export const OperatorsCards: React.FC = ({ operatorOwner, f {orderedOperators.length === 0 ? [0].map((_, key) => ( - No operators found + {t('components.operatorsCards.noRow')} {subspaceAccount === operatorOwner && ( - - If you recently register a operator, it may take up to 10 minutes for the operator to be added. - + {t('components.operatorsCards.delayWarning')} )} )) @@ -70,7 +72,7 @@ export const OperatorsCards: React.FC = ({ operatorOwner, f - operatorId #{operator.operatorId} + {t('components.operatorsCards.operatorId', { operatorId: operator.operatorId })} {formatAddress(operator.operatorDetail.signingKey)} @@ -85,17 +87,17 @@ export const OperatorsCards: React.FC = ({ operatorOwner, f - {nominatorsCount} Nominators + {t('components.operatorsCards.nominators', { nominatorsCount })} )} - Nominator Tax + {t('components.operatorsCards.nominatorTax')} {operator.operatorDetail.nominationTax}% - Min Nominator Stake + {t('components.operatorsCards.minNominatorsStake')} {hexToFormattedNumber(operator.operatorDetail.minimumNominatorStake)} @@ -103,7 +105,7 @@ export const OperatorsCards: React.FC = ({ operatorOwner, f - Funds in stake + {t('components.operatorsCards.fundsInStake')} {hexToFormattedNumber(operator.operatorDetail.currentTotalStake)} diff --git a/src/components/operatorsList.tsx b/src/components/operatorsList.tsx index 8c7ee57..7f200a4 100644 --- a/src/components/operatorsList.tsx +++ b/src/components/operatorsList.tsx @@ -1,5 +1,6 @@ import { Box, HStack, Heading, Table, TableContainer, Tbody, Td, Text, Th, Thead, Tr } from '@chakra-ui/react' import { encodeAddress } from '@polkadot/keyring' +import { useTranslation } from 'next-i18next' import Link from 'next/link' import React, { useEffect, useState } from 'react' import { ROUTES, headingStyles, tHeadStyles, tableStyles, textStyles } from '../constants' @@ -16,6 +17,7 @@ interface OperatorsListProps { } export const OperatorsList: React.FC = ({ operatorOwner, fromManage }) => { + const { t } = useTranslation() const [clientSide, setClientSide] = useState(false) const { extension, @@ -35,9 +37,11 @@ export const OperatorsList: React.FC = ({ operatorOwner, fro - Information across operators + {t('components.operatorsList.header')} {operatorOwner && ( - on Account {formatAddress(operatorOwner)} + + {t('components.operatorsList.headerOperatorOwner', { account: formatAddress(operatorOwner) })} + )} @@ -46,14 +50,14 @@ export const OperatorsList: React.FC = ({ operatorOwner, fro - - - - - - - {subspaceAccount && } + + + + + + + + {subspaceAccount && } {orderedOperators.length === 0 ? ( @@ -61,11 +65,9 @@ export const OperatorsList: React.FC = ({ operatorOwner, fro {[0].map((_, key) => ( diff --git a/src/components/operatorsTotal.tsx b/src/components/operatorsTotal.tsx index 6415a6e..258f62d 100644 --- a/src/components/operatorsTotal.tsx +++ b/src/components/operatorsTotal.tsx @@ -1,4 +1,5 @@ import { Box, Grid, GridItem, HStack, Heading, Text } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' import React from 'react' import { headingStyles, textStyles } from '../constants' import { useTotal } from '../hooks/useTotal' @@ -11,6 +12,7 @@ interface OperatorsTotalProps { } export const OperatorsTotal: React.FC = ({ operatorOwner }) => { + const { t } = useTranslation() const { chainDetails: { tokenSymbol } } = useExtension() @@ -27,30 +29,32 @@ export const OperatorsTotal: React.FC = ({ operatorOwner }) - Aggregated data + {t('components.operatorsTotal.header')} {operatorOwner && ( - on Account {formatAddress(operatorOwner)} + + {t('components.operatorsTotal.headerOperatorOwner', { account: formatAddress(operatorOwner) })} + )} - Funds in Stake, {tokenSymbol} + {t('components.operatorsTotal.fundsInStake', { tokenSymbol })} {formatNumber(totalFundsInStake)} - Number of Operators + {t('components.operatorsTotal.operatorsCount')} {totalOperators} - Number of Nominators + {t('components.operatorsTotal.nominatorsCount')} {totalNominators} - Available for withdrawal, {tokenSymbol} + {t('components.operatorsTotal.availableForWithdrawal', { tokenSymbol })} {formatNumber(totalFundsInStakeAvailable)} @@ -58,14 +62,14 @@ export const OperatorsTotal: React.FC = ({ operatorOwner }) - Operator’s funds, {tokenSymbol} + {t('components.operatorsTotal.operatorsFunds', { tokenSymbol })} {formatNumber(totalOperatorsStake)} - Nominator’s funds, {tokenSymbol} + {t('components.operatorsTotal.nominatorsFunds', { tokenSymbol })} {formatNumber(totalNominatorsStake)} diff --git a/src/components/pieGraph.tsx b/src/components/pieGraph.tsx index 8e17790..2674d19 100644 --- a/src/components/pieGraph.tsx +++ b/src/components/pieGraph.tsx @@ -1,5 +1,6 @@ import { Box, Grid, GridItem, Heading } from '@chakra-ui/react' import { ResponsivePie } from '@nivo/pie' +import { useTranslation } from 'next-i18next' import React from 'react' import { headingStyles } from '../constants' import { useTotal } from '../hooks/useTotal' @@ -107,6 +108,7 @@ interface OperatorsTotalProps { } export const PieGraph: React.FC = ({ operatorOwner, small }) => { + const { t } = useTranslation() const { totalOperators, totalOperatorsStake, totalNominators, totalNominatorsStake } = useTotal(operatorOwner) return ( @@ -117,9 +119,9 @@ export const PieGraph: React.FC = ({ operatorOwner, small } mt={small ? 0 : 6}> {small ? ( - Total stake + {t('components.pieGraph.totalStake')} ) : ( - Total stake + {t('components.pieGraph.totalStake')} )} = ({ operatorOwner, small } {small ? ( - Quantity + {t('components.pieGraph.quantity')} ) : ( - Quantity + {t('components.pieGraph.quantity')} )} = ({ heading, description, hash }) => { + const { t } = useTranslation() + return (
@@ -19,7 +22,7 @@ export const SuccessTxToast: React.FC = ({ heading, descrip {description} diff --git a/src/components/transactionsSpotter.tsx b/src/components/transactionsSpotter.tsx index 420e6a4..c9e7c36 100644 --- a/src/components/transactionsSpotter.tsx +++ b/src/components/transactionsSpotter.tsx @@ -1,5 +1,6 @@ import { CheckIcon, ChevronDownIcon, HamburgerIcon, WarningTwoIcon } from '@chakra-ui/icons' import { Button, HStack, Menu, MenuButton, MenuItem, MenuList, Spinner, Text, VStack } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' import React, { useCallback, useEffect, useMemo } from 'react' import { ActionType, MAX_BLOCKS_TO_FETCH_FOR_TRANSACTIONS_SPOTTER, SUBSCAN_URL, TransactionStatus } from '../constants' import { useExtension, useTransactions } from '../states/extension' @@ -7,6 +8,7 @@ import { Transaction } from '../types' import { capitalizeFirstLetter } from '../utils' export const TransactionsSpotter: React.FC = () => { + const { t } = useTranslation() const { api, subspaceAccount, @@ -131,11 +133,11 @@ export const TransactionsSpotter: React.FC = () => { ) : ( - No transactions yet + {t('components.transactionsSpotter.noTxYet')} ), - [transactionDetails, userTransactions] + [t, transactionDetails, userTransactions] ) const transactionButton = useMemo(() => { diff --git a/src/components/viewSelector.tsx b/src/components/viewSelector.tsx index 8bae2a4..cd71f52 100644 --- a/src/components/viewSelector.tsx +++ b/src/components/viewSelector.tsx @@ -2,6 +2,7 @@ import { DragHandleIcon, HamburgerIcon, UpDownIcon } from '@chakra-ui/icons' import { ButtonGroup, HStack, IconButton, Spacer, Text } from '@chakra-ui/react' import type { SingleValue } from 'chakra-react-select' import { Select } from 'chakra-react-select' +import { useTranslation } from 'next-i18next' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { ViewOrderBy, ViewOrderDirection, textStyles } from '../constants' import { useView } from '../states/view' @@ -9,6 +10,7 @@ import { Option } from '../types' import { capitalizeFirstLetter } from '../utils' export const ViewSelector: React.FC = () => { + const { t } = useTranslation() const [clientSide, setClientSide] = useState(false) const { isMobile, @@ -69,13 +71,21 @@ export const ViewSelector: React.FC = () => { <> View - } /> - } /> + } + /> + } + /> )} - Order by + {t('components.viewSelector.orderBy')} + +
Nominator AccountOperatorIDNominator TaxMin Nominator StakeFunds stake% of ShareTotal Funds stakeActions{t('components.nominatorsList.nominatorAccount')}{t('components.nominatorsList.operatorID')}{t('components.nominatorsList.nominatorTax')}{t('components.nominatorsList.minNominatorsStake')}{t('components.nominatorsList.fundsInStake')}{t('components.nominatorsList.pcOfStake')}{t('components.nominatorsList.totalFundsStaked')}{t('action.actions')}
- No nominators found + {t('components.nominatorsList.noRow')}
- OperatorIDSigning keyOperator AccountNominators CountNominator TaxMin Nominator StakeFunds in stakeActions{t('components.operatorsList.table.operatorId')}{t('components.operatorsList.table.signingKey')}{t('components.operatorsList.table.operatorAccount')}{t('components.operatorsList.table.nominatorsCount')}{t('components.operatorsList.table.nominatorTax')}{t('components.operatorsList.table.minNominatorStake')}{t('components.operatorsList.table.fundsInStake')}{t('action.actions')}
- No operators found + {t('components.operatorsList.noRow')} {subspaceAccount === operatorOwner && ( - - If you recently register a operator, it may take up to 10 minutes for the operator to be added. - + {t('components.operatorsList.delayWarning')} )}