Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

Allowing for multi-lang web app #84

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions next-i18next.config.js
Original file line number Diff line number Diff line change
@@ -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'
}
4 changes: 3 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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]
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
228 changes: 228 additions & 0 deletions public/locales/en/common.json
Original file line number Diff line number Diff line change
@@ -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."
}
}
}
32 changes: 19 additions & 13 deletions src/components/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -35,6 +36,7 @@ interface ActionsProps {
}

export const Actions: React.FC<ActionsProps> = ({ operatorId }) => {
const { t } = useTranslation()
const finalRef = useRef(null)
const { isOpen, onOpen, onClose } = useDisclosure()
const { subspaceAccount, stakingConstants, chainDetails } = useExtension()
Expand Down Expand Up @@ -89,7 +91,7 @@ export const Actions: React.FC<ActionsProps> = ({ operatorId }) => {
<>
<Menu>
<MenuButton {...actionButtonStyles} as={Button} rightIcon={<ChevronDownIcon pl='2' />}>
Action
{t('components.actions.header')}
</MenuButton>
<MenuList>
{ActionsList.map((action) => (
Expand All @@ -111,42 +113,46 @@ export const Actions: React.FC<ActionsProps> = ({ operatorId }) => {
<ModalOverlay />
<ModalContent>
<ModalHeader>
{actionSelected != null && capitalizeFirstLetter(actionSelected)} on Operator Id #{operatorId}
{actionSelected != null &&
t('components.actions.operatorHeader', {
actionSelected: capitalizeFirstLetter(actionSelected),
operatorId
})}
</ModalHeader>
<ModalCloseButton />
<ModalBody p={4}>
<VStack w='100%' align='left'>
<Text fontWeight='500'>Operator Account</Text>
<Text fontWeight='500'>{t('components.actions.operatorAccount')}</Text>
<Text>{operator && operator.operatorOwner}</Text>
<Text fontWeight='500'>Signing Key</Text>
<Text fontWeight='500'>{t('components.actions.signingKey')}</Text>
<Text fontSize='12px'>
{operator && (
<Link href={`${ROUTES.OPERATOR_STATS}/${operator.operatorDetail.signingKey}`}>
{operator.operatorDetail.signingKey}
</Link>
)}
</Text>
{actionSelected === ActionType.Deregister && <Text>Do you really want to de-register as operator?</Text>}
{actionSelected === ActionType.Deregister && <Text>{t('components.actions.confirmDeRegistration')}</Text>}
{actionSelected === ActionType.AddFunds && (
<FormControl isInvalid={isErrorsField[ActionType.AddFunds]}>
<InputGroup size='md' mt='4' w='100%'>
<Input
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' }}
/>
<InputRightElement>
<Button m={1} onClick={handleMaxAmountToAddFunds} size='sm'>
Max
{t('action.max')}
</Button>
</InputRightElement>
</InputGroup>
{isErrorsField[ActionType.AddFunds] ? (
<FormErrorMessage h='10'>The amount you enter is not valid</FormErrorMessage>
<FormErrorMessage h='10'>{t('components.actions.field.amount.error')}</FormErrorMessage>
) : (
<FormHelperText h='10'></FormHelperText>
)}
Expand All @@ -159,19 +165,19 @@ export const Actions: React.FC<ActionsProps> = ({ 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' }}
/>
<InputRightElement>
<Button m={1} onClick={handleMaxAmountToWithdraw} size='sm'>
Max
{t('action.max')}
</Button>
</InputRightElement>
</InputGroup>
{isErrorsField[ActionType.Withdraw] ? (
<FormErrorMessage h='10'>The amount you enter is not valid</FormErrorMessage>
<FormErrorMessage h='10'>{t('components.actions.field.amount.error')}</FormErrorMessage>
) : (
<FormHelperText h='10'></FormHelperText>
)}
Expand All @@ -182,10 +188,10 @@ export const Actions: React.FC<ActionsProps> = ({ operatorId }) => {

<ModalFooter>
<Button variant='outline' colorScheme='brand' mr={3} onClick={handleCloseModal}>
Close
{t('action.close')}
</Button>
<Button colorScheme='brand' mr={3} onClick={handleClickSubmit}>
Submit
{t('action.submit')}
</Button>
</ModalFooter>
</ModalContent>
Expand Down
Loading