diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..de4d1f007 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/.eslintrc.js b/.eslintrc.js index 6a41934a4..e88c2f0ee 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ module.exports = { extends: ['airbnb', 'plugin:prettier/recommended'], parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint', 'react-hooks'], + plugins: ['@typescript-eslint', 'react-hooks', 'unused-imports'], globals: { State: 'true', CustomRouter: 'true', @@ -32,16 +32,10 @@ module.exports = { }, ], 'no-unused-vars': 'off', + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': 'error', 'no-undef': 'off', 'implicit-arrow-linebreak': 'off', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - vars: 'local', - args: 'after-used', - ignoreRestSiblings: false, - }, - ], 'operator-linebreak': [0, 'none'], 'arrow-parens': [2, 'as-needed'], 'max-len': [ diff --git a/package.json b/package.json index de1db4932..b892a8aec 100644 --- a/package.json +++ b/package.json @@ -56,11 +56,12 @@ "eslint": "7.32.0", "eslint-config-airbnb": "18.2.1", "eslint-config-prettier": "9.0.0", - "eslint-plugin-import": "2.28.1", + "eslint-plugin-import": "^2.28.1", "eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-prettier": "3.4.1", "eslint-plugin-react": "7.31.11", "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-unused-imports": "^3.0.0", "husky": "^7.0.1", "jest-styled-components": "^7.0.5", "mockdate": "^2.0.5", diff --git a/src/__tests__/utils/date.test.ts b/src/__tests__/utils/date.test.ts index 65e0c9c0c..d98ea36a3 100644 --- a/src/__tests__/utils/date.test.ts +++ b/src/__tests__/utils/date.test.ts @@ -11,7 +11,6 @@ import { parseSimpleDate, parseSimpleDateNoSecond, getCurrentYear, - parseDate, getCSTTime, } from '../../utils/date' @@ -83,16 +82,6 @@ describe('Date methods tests', () => { expect(getCurrentYear()).toBe(2020) }) - it('parseDate', async () => { - MockDate.set(1588694400000, 480) - expect(parseDate(1588694380000)).toBe('20s ago') - expect(parseDate(1588691000000)).toBe('56min 40s ago') - - MockDate.reset() - timezoneMock.register('UTC') - expect(parseDate(1588651000000)).toBe('2020/05/05 03:56:40') - }) - it('getCSTTime', async () => { timezoneMock.register('UTC') expect(parseSimpleDate(1588651000000)).toBe('2020/05/05 03:56:40') diff --git a/src/assets/X.svg b/src/assets/X.svg new file mode 100644 index 000000000..471020f56 --- /dev/null +++ b/src/assets/X.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/block-rewards-cn.png b/src/assets/block-rewards-cn.png new file mode 100644 index 000000000..87d0ded7f Binary files /dev/null and b/src/assets/block-rewards-cn.png differ diff --git a/src/assets/block-rewards.png b/src/assets/block-rewards.png new file mode 100644 index 000000000..aee406a38 Binary files /dev/null and b/src/assets/block-rewards.png differ diff --git a/src/assets/calendar.svg b/src/assets/calendar.svg new file mode 100644 index 000000000..1332af123 --- /dev/null +++ b/src/assets/calendar.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/ckb_base_issuance_trend.png b/src/assets/ckb_base_issuance_trend.png new file mode 100644 index 000000000..1001b87ea Binary files /dev/null and b/src/assets/ckb_base_issuance_trend.png differ diff --git a/src/assets/fonts/digital-7.ttf b/src/assets/fonts/digital-7.ttf new file mode 100644 index 000000000..5dbe6f908 Binary files /dev/null and b/src/assets/fonts/digital-7.ttf differ diff --git a/src/assets/fonts/fonts.css b/src/assets/fonts/fonts.css index e32583b1d..a5dfb50c6 100644 --- a/src/assets/fonts/fonts.css +++ b/src/assets/fonts/fonts.css @@ -4,3 +4,8 @@ font-weight: 300; font-style: normal; } + +@font-face { + font-family:digital-clock-font; + src: url('./digital-7.ttf'); +} diff --git a/src/assets/halving_banner.png b/src/assets/halving_banner.png new file mode 100644 index 000000000..fe31acbe7 Binary files /dev/null and b/src/assets/halving_banner.png differ diff --git a/src/assets/halving_banner_success.png b/src/assets/halving_banner_success.png new file mode 100644 index 000000000..ab7d607e0 Binary files /dev/null and b/src/assets/halving_banner_success.png differ diff --git a/src/assets/halving_banner_success_mobile.png b/src/assets/halving_banner_success_mobile.png new file mode 100644 index 000000000..049948c33 Binary files /dev/null and b/src/assets/halving_banner_success_mobile.png differ diff --git a/src/assets/halving_bg.png b/src/assets/halving_bg.png new file mode 100644 index 000000000..72a553fc0 Binary files /dev/null and b/src/assets/halving_bg.png differ diff --git a/src/assets/halving_success_ani.gif b/src/assets/halving_success_ani.gif new file mode 100644 index 000000000..aacfdacf1 Binary files /dev/null and b/src/assets/halving_success_ani.gif differ diff --git a/src/assets/halving_success_bg.png b/src/assets/halving_success_bg.png new file mode 100644 index 000000000..13a4e036d Binary files /dev/null and b/src/assets/halving_success_bg.png differ diff --git a/src/assets/move.svg b/src/assets/move.svg new file mode 100644 index 000000000..11b7f12e9 --- /dev/null +++ b/src/assets/move.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/warning_circle.svg b/src/assets/warning_circle.svg new file mode 100644 index 000000000..23281be15 --- /dev/null +++ b/src/assets/warning_circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/components/Banner/HalvingBanner.tsx b/src/components/Banner/HalvingBanner.tsx new file mode 100644 index 000000000..bc58cc75a --- /dev/null +++ b/src/components/Banner/HalvingBanner.tsx @@ -0,0 +1,119 @@ +import classnames from 'classnames' +import { useTranslation } from 'react-i18next' +import styles from './index.module.scss' +import halvingBanner from '../../assets/halving_banner.png' +import halvingBannerSuccess from '../../assets/halving_banner_success.png' +import halvingBannerSuccessMobile from '../../assets/halving_banner_success_mobile.png' +import { ReactComponent as MoveIcon } from '../../assets/move.svg' +import LoadingWhiteImage from '../../assets/loading_white.gif' +import halvingSuccessAni from '../../assets/halving_success_ani.gif' +import SimpleButton from '../SimpleButton' +import { useCountdown, useHalving, useIsMobile } from '../../utils/hook' + +function numberToOrdinal(number: number) { + switch (number) { + case 1: + return 'first' + case 2: + return 'second' + default: + break + } + + switch (number % 10) { + case 1: + return `${number}st` + case 2: + return `${number}nd` + case 3: + return `${number}rd` + default: + return `${number}th` + } +} + +export const HalvingBanner = () => { + const { estimatedDate, halvingCount, inCelebration, isLoading } = useHalving() + const [days, hours, minutes, seconds, expired] = useCountdown(estimatedDate) + const isMobile = useIsMobile() + const [t, { language }] = useTranslation() + + const shortCountdown = () => { + if (isLoading || Number.isNaN(seconds)) { + return loading + } + if (days > 0) { + return `${days}${t('symbol.char_space')}${t('unit.days')}` + } + if (hours > 0) { + return `${hours}${t('symbol.char_space')}${t('unit.hours')}` + } + if (minutes > 0) { + return `${minutes}${t('symbol.char_space')}${t('unit.minutes')}` + } + + return `${seconds}${t('symbol.char_space')}${t('unit.seconds')}` + } + + const learnMoreText = () => { + if (inCelebration) { + return t('halving.learn_more') + } + + if (expired) { + return t('halving.comming_soon') + } + + return ( + <> + {t('halving.halving_countdown')} {shortCountdown()} + + ) + } + + const bgImage = (() => { + if (!inCelebration) { + return halvingBanner + } + + if (isMobile) { + return halvingBannerSuccessMobile + } + + return halvingBannerSuccess + })() + + return ( +
+
+
+ {inCelebration && animation} + {inCelebration ? ( +
+ {t('halving.banner_congratulation', { + times: t(`ordinal.${numberToOrdinal(halvingCount)}`), + }).toUpperCase()} +
+ ) : ( +
+ {`Nervos CKB ${t(`ordinal.${numberToOrdinal(halvingCount)}`)}${language === 'en' ? ' ' : ''}${t( + 'halving.halving', + )}`} +
+ )} + + + {learnMoreText()} + + + +
+
+
+ ) +} diff --git a/src/components/Banner/index.module.scss b/src/components/Banner/index.module.scss index 3df43da4e..2703ae03e 100644 --- a/src/components/Banner/index.module.scss +++ b/src/components/Banner/index.module.scss @@ -14,3 +14,108 @@ $backgroudColor: #232323; background-image: url('../../assets/ckb_explorer_banner_phone.svg'); } } + +.halvingBannerSuccess { + background-size: cover; + background-position: center; +} + +.halvingBannerWrapper { + width: 100%; + background-color: $backgroudColor; + height: 200px; + position: relative; + background-repeat: no-repeat; + background-position: bottom; + background-size: cover; + + @media (width >= 750px) { + height: 300px; + } +} + +.halvingBannerShadow { + width: 100%; + background-color: rgb(0 0 0 / 30%); + height: 100%; +} + +.halvingLoading { + width: 20px; + margin-top: 4px; +} + +.halvingBanner { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 8px; + + @media (width >= 750px) { + gap: 16px; + } +} + +.halvingBannerAnimation { + margin-top: -16px; + height: 64px; + + @media (width >= 750px) { + height: 100px; + } +} + +.halvingBannerText { + align-items: baseline; + display: flex; + font-size: 24px; + font-weight: bold; + text-align: center; + text-transform: capitalize; + + &.linear { + background: linear-gradient(45deg, rgb(130 203 140), rgb(121 122 121), rgb(172 221 181)); + background-clip: text; + -webkit-text-fill-color: transparent; + } + + &.success { + transform: rotate(-2deg); + text-shadow: 1.5px 1.5px 1px #57f3a9, -1px -1px 1px #eef8f2; + color: rgb(28 28 28); + line-height: 26px; + margin-top: -4px; + margin-bottom: 4px; + + @media (width >= 750px) { + text-shadow: 1.5px 1.5px 1px #57f3a9, -1px -1px 1px #eef8f2; + } + } + + @media (width >= 750px) { + font-size: 40px; + } +} + +.halvingBannerCount { + font-size: 36px; + margin-left: 24px; +} + +.learnMoreButton { + border: 0; + border-radius: 8px; + color: white; + padding: 8px 16px; + user-select: none; + text-transform: capitalize; + background: linear-gradient(180deg, #00cc9b, #008365); + display: flex; + align-items: center; + + @media (width >= 750px) { + padding: 16px 24px; + } +} diff --git a/src/components/Card/HashCard/index.tsx b/src/components/Card/HashCard/index.tsx index 0f1ce4d34..e2831e6f8 100644 --- a/src/components/Card/HashCard/index.tsx +++ b/src/components/Card/HashCard/index.tsx @@ -1,11 +1,10 @@ import type { FC, ReactNode } from 'react' import { Link } from 'react-router-dom' import { Radio, Tooltip } from 'antd' +import { useTranslation } from 'react-i18next' import { LayoutLiteProfessional } from '../../../constants/common' import CopyIcon from '../../../assets/copy.png' -import i18n from '../../../utils/i18n' import { explorerService } from '../../../services/ExplorerService' -import { copyElementValue } from '../../../utils/util' import SmallLoading from '../../Loading/SmallLoading' import { useIsMobile, useNewAddr, useDeprecatedAddr, useSearchParams, useUpdateSearchParams } from '../../../utils/hook' import SimpleButton from '../../SimpleButton' @@ -52,7 +51,9 @@ export default ({ const isMobile = useIsMobile() const { Professional, Lite } = LayoutLiteProfessional const setToast = useSetToast() - const isTx = i18n.t('transaction.transaction') === title + const { t } = useTranslation() + + const isTx = t('transaction.transaction') === title const newAddr = useNewAddr(hash) const deprecatedAddr = useDeprecatedAddr(hash) const counterpartAddr = newAddr === hash ? deprecatedAddr : newAddr @@ -116,8 +117,8 @@ export default ({ { - copyElementValue(document.getElementById('hashValue')) - setToast({ message: i18n.t('common.copied') }) + navigator.clipboard.writeText(hash) + setToast({ message: t('common.copied') }) }} > {!loading && copy} @@ -125,7 +126,7 @@ export default ({ {counterpartAddr ? ( ) : null} - {isTx && !loading ? ( - + {isTx ? ( + @@ -151,8 +152,8 @@ export default ({ onChangeLayout(value)} value={layout} @@ -167,9 +168,9 @@ export default ({ )} {specialAddress && ( - + - {i18n.t('address.vesting')} + {t('address.vesting')} )} @@ -183,8 +184,8 @@ export default ({ onChangeLayout(value)} value={layout} diff --git a/src/components/DecimalCapacity/index.tsx b/src/components/DecimalCapacity/index.tsx index 5e21ea48e..a45171cab 100644 --- a/src/components/DecimalCapacity/index.tsx +++ b/src/components/DecimalCapacity/index.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames' -import i18n from '../../utils/i18n' +import { useTranslation } from 'react-i18next' import { DecimalPanel, DecimalPartPanel, DecimalZerosPanel } from './styled' import styles from './styles.module.scss' @@ -18,6 +18,7 @@ export default ({ hideZero?: boolean marginBottom?: string }) => { + const { t } = useTranslation() const integer = value.split('.')[0] || '0' const isPayment = balanceChangeType === 'payment' const balanceChangeTypeClass = isPayment ? 'subtraction' : 'addition' @@ -53,9 +54,7 @@ export default ({ {zeros} )} - {!hideUnit && ( -
{i18n.t('common.ckb_unit')}
- )} + {!hideUnit &&
{t('common.ckb_unit')}
} ) } diff --git a/src/components/Dropdown/Language/index.tsx b/src/components/Dropdown/Language/index.tsx index b1c4e3971..93385d7d5 100644 --- a/src/components/Dropdown/Language/index.tsx +++ b/src/components/Dropdown/Language/index.tsx @@ -1,30 +1,24 @@ -import i18n, { currentLanguage, changeLanguage } from '../../../utils/i18n' +import { useLanguageText, useOtherLanguageText, useToggleLanguage } from '../../../utils/i18n' import { LanguagePanel } from './styled' import SimpleButton from '../../SimpleButton' -export const languageText = (lan: 'en' | 'zh' | null, reverse?: boolean) => { - if (reverse) { - return lan === 'zh' ? i18n.t('navbar.language_en') : i18n.t('navbar.language_zh') - } - return lan === 'en' ? i18n.t('navbar.language_en') : i18n.t('navbar.language_zh') -} - export default ({ setShow, left, top }: { setShow: Function; left: number; top: number }) => { + const toggleLanguage = useToggleLanguage() const hideDropdown = () => { setShow(false) } const handleLanguage = () => { hideDropdown() - changeLanguage(currentLanguage() === 'en' ? 'zh' : 'en') + toggleLanguage() } return ( - {languageText(currentLanguage())} + {useLanguageText()}
- {languageText(currentLanguage(), true)} + {useOtherLanguageText()} ) diff --git a/src/components/Header/LanguageComp/index.tsx b/src/components/Header/LanguageComp/index.tsx index 0db7afa8d..22fbc17f9 100644 --- a/src/components/Header/LanguageComp/index.tsx +++ b/src/components/Header/LanguageComp/index.tsx @@ -1,5 +1,4 @@ import { useState, useLayoutEffect, FC } from 'react' -import i18n, { currentLanguage, changeLanguage } from '../../../utils/i18n' import { HeaderLanguagePanel, MobileSubMenuPanel } from './styled' import SimpleButton from '../../SimpleButton' import WhiteDropdownIcon from '../../../assets/white_dropdown.png' @@ -7,7 +6,8 @@ import WhiteDropUpIcon from '../../../assets/white_drop_up.png' import BlueDropUpIcon from '../../../assets/blue_drop_up.png' import GreenDropUpIcon from '../../../assets/green_drop_up.png' import { isMainnet } from '../../../utils/chain' -import LanDropdown, { languageText } from '../../Dropdown/Language' +import LanDropdown from '../../Dropdown/Language' +import { useCurrentLanguage, useLanguageText, useOtherLanguageText, useToggleLanguage } from '../../../utils/i18n' const getDropdownIcon = (showDropdown: boolean) => { if (!showDropdown) return WhiteDropdownIcon @@ -18,6 +18,7 @@ export const LanguageDropdown = () => { const [showLanguage, setShowLanguage] = useState(false) const [languageLeft, setLanguageLeft] = useState(0) const [languageTop, setLanguageTop] = useState(0) + const currentLanguage = useCurrentLanguage() useLayoutEffect(() => { if (showLanguage) { @@ -25,12 +26,12 @@ export const LanguageDropdown = () => { if (languageDropdownComp) { const languageDropdownReact = languageDropdownComp.getBoundingClientRect() if (languageDropdownReact) { - setLanguageLeft(languageDropdownReact.left + (currentLanguage() === 'en' ? -15 : 3)) + setLanguageLeft(languageDropdownReact.left + (currentLanguage === 'en' ? -15 : 3)) setLanguageTop(languageDropdownReact.bottom - 3) } } } - }, [showLanguage]) + }, [showLanguage, currentLanguage]) return ( { }} >
-
{languageText(currentLanguage())}
+
{useLanguageText()}
dropdown icon
@@ -58,6 +59,9 @@ export const LanguageDropdown = () => { export const LanguageMenu: FC<{ hideMobileMenu: () => void }> = ({ hideMobileMenu }) => { const [showSubMenu, setShowSubMenu] = useState(false) + const currentLanguageText = useLanguageText() + const otherLanguageText = useOtherLanguageText() + const toggleLanguage = useToggleLanguage() return ( @@ -67,9 +71,7 @@ export const LanguageMenu: FC<{ hideMobileMenu: () => void }> = ({ hideMobileMen setShowSubMenu(!showSubMenu) }} > -
- {currentLanguage() === 'en' ? i18n.t('navbar.language_en') : i18n.t('navbar.language_zh')} -
+
{currentLanguageText}
mobile language icon void }> = ({ hideMobileMen hideMobileMenu() }} > - {currentLanguage() === 'en' ? i18n.t('navbar.language_en') : i18n.t('navbar.language_zh')} + {currentLanguageText} { - changeLanguage(currentLanguage() === 'en' ? 'zh' : 'en') + toggleLanguage() hideMobileMenu() }} > - {currentLanguage() === 'en' ? i18n.t('navbar.language_zh') : i18n.t('navbar.language_en')} + {otherLanguageText} )} diff --git a/src/components/Header/MenusComp/index.tsx b/src/components/Header/MenusComp/index.tsx index ca80e26f4..40bee6c88 100644 --- a/src/components/Header/MenusComp/index.tsx +++ b/src/components/Header/MenusComp/index.tsx @@ -1,7 +1,7 @@ import { Link } from 'react-router-dom' import { memo } from 'react' +import { useTranslation } from 'react-i18next' import { useIsMobile } from '../../../utils/hook' -import i18n from '../../../utils/i18n' import { MobileMenuItem, MobileMenuLink, HeaderMenuPanel } from './styled' import { isMainnet } from '../../../utils/chain' @@ -10,45 +10,48 @@ export enum LinkType { Outer, } -const menuDataList = () => [ - { - type: LinkType.Inner, - name: i18n.t('navbar.home'), - url: '/', - }, - { - type: LinkType.Inner, - name: i18n.t('navbar.nervos_dao'), - url: '/nervosdao', - }, - { - type: LinkType.Inner, - name: i18n.t('navbar.tokens'), - url: '/tokens', - }, - { - type: LinkType.Inner, - name: i18n.t('navbar.nft_collections'), - url: '/nft-collections', - }, - { - type: LinkType.Inner, - name: i18n.t('navbar.charts'), - url: '/charts', - }, - { - type: LinkType.Inner, - name: i18n.t('navbar.fee_rate'), - url: '/fee-rate-tracker', - }, - !isMainnet() - ? { - type: LinkType.Outer, - name: i18n.t('navbar.faucet'), - url: 'https://faucet.nervos.org/', - } - : {}, -] +const useMenuDataList = () => { + const { t } = useTranslation() + return [ + { + type: LinkType.Inner, + name: t('navbar.home'), + url: '/', + }, + { + type: LinkType.Inner, + name: t('navbar.nervos_dao'), + url: '/nervosdao', + }, + { + type: LinkType.Inner, + name: t('navbar.tokens'), + url: '/tokens', + }, + { + type: LinkType.Inner, + name: t('navbar.nft_collections'), + url: '/nft-collections', + }, + { + type: LinkType.Inner, + name: t('navbar.charts'), + url: '/charts', + }, + { + type: LinkType.Inner, + name: t('navbar.fee_rate'), + url: '/fee-rate-tracker', + }, + !isMainnet() + ? { + type: LinkType.Outer, + name: t('navbar.faucet'), + url: 'https://faucet.nervos.org/', + } + : {}, + ] +} const MenuItemLink = ({ menu }: { menu: any }) => { const { url, type, name } = menu @@ -59,10 +62,11 @@ const MenuItemLink = ({ menu }: { menu: any }) => { ) } -export default memo(() => - useIsMobile() ? ( +export default memo(() => { + const menuList = useMenuDataList() + return useIsMobile() ? ( - {menuDataList() + {menuList .filter(menu => menu.name !== undefined) .map(menu => ( @@ -70,7 +74,7 @@ export default memo(() => ) : ( - {menuDataList() + {menuList .filter(menu => menu.name !== undefined) .map(menu => menu.type === LinkType.Inner ? ( @@ -84,5 +88,5 @@ export default memo(() => ), )} - ), -) + ) +}) diff --git a/src/components/MaintainAlert/index.tsx b/src/components/MaintainAlert/index.tsx index 77e395e14..3cead3e90 100644 --- a/src/components/MaintainAlert/index.tsx +++ b/src/components/MaintainAlert/index.tsx @@ -1,10 +1,11 @@ -import i18n from '../../utils/i18n' +import { useTranslation } from 'react-i18next' import { IS_MAINTAINING } from '../../constants/common' import styles from './styles.module.scss' const MaintainAlert = () => { + const { t } = useTranslation() if (IS_MAINTAINING) { - return
{i18n.t('error.maintain')}
+ return
{t('error.maintain')}
} return null diff --git a/src/components/NftCollectionInventory/index.tsx b/src/components/NftCollectionInventory/index.tsx index f21baff5a..ab6747b02 100644 --- a/src/components/NftCollectionInventory/index.tsx +++ b/src/components/NftCollectionInventory/index.tsx @@ -3,9 +3,9 @@ import { Link } from 'react-router-dom' import { useQuery } from 'react-query' import { Base64 } from 'js-base64' import { hexToBytes } from '@nervosnetwork/ckb-sdk-utils' +import { useTranslation } from 'react-i18next' import { parseSporeCellData } from '../../utils/spore' import { ReactComponent as Cover } from '../../assets/nft_cover.svg' -import i18n from '../../utils/i18n' import styles from './styles.module.scss' import { getPrimaryColor } from '../../constants/common' import { explorerService } from '../../services/ExplorerService' @@ -32,6 +32,7 @@ const NftCollectionInventory: React.FC<{ collection: string isLoading: boolean }> = ({ list, collection, isLoading }) => { + const { t } = useTranslation() const { data: info } = useQuery>(['collection-info', collection], () => explorerService.api.requesterV2(`nft/collections/${collection}`), ) @@ -47,7 +48,7 @@ const NftCollectionInventory: React.FC<{ gridTemplateColumns: 'auto', }} > - {isLoading ? i18n.t('nft.loading') : i18n.t(`nft.no_record`)} + {isLoading ? t('nft.loading') : t(`nft.no_record`)}
) } @@ -107,7 +108,7 @@ const NftCollectionInventory: React.FC<{
- {i18n.t(`nft.owner`)} + {t(`nft.owner`)} {item.owner ? ( { + const { t } = useTranslation() const { isLoading, data } = useQuery>(['collection-info', id], () => explorerService.api.requesterV2(`nft/collections/${id}`), ) @@ -41,30 +42,30 @@ const NftCollectionOverview = ({ id }: { id: string }) => { ) : ( logo )} - {isLoading ? i18n.t(`nft.loading`) : info?.name} + {isLoading ? t(`nft.loading`) : info?.name}
{info?.description}
-
{i18n.t(`nft.standard`)}
+
{t(`nft.standard`)}
- {isLoading ? i18n.t(`nft.loading`) : null} - {!isLoading && info?.standard ? i18n.t(`nft.${info?.standard}`) : `-`} + {isLoading ? t(`nft.loading`) : null} + {!isLoading && info?.standard ? t(`nft.${info?.standard}`) : `-`}
- {i18n.t('nft.holder')}/{i18n.t('nft.minted')} + {t('nft.holder')}/{t('nft.minted')}
{isLoading - ? i18n.t(`nft.loading`) + ? t(`nft.loading`) : `${(info?.holders_count ?? 0).toLocaleString('en')}/${(info?.items_count ?? 0).toLocaleString('en')}`}
-
{i18n.t(`nft.minter_address`)}
+
{t(`nft.minter_address`)}
- {isLoading ? i18n.t(`nft.loading`) : null} + {isLoading ? t(`nft.loading`) : null} {!isLoading && info?.creator ? ( diff --git a/src/components/NftCollectionTransfers/index.tsx b/src/components/NftCollectionTransfers/index.tsx index 952889f79..596a4810e 100644 --- a/src/components/NftCollectionTransfers/index.tsx +++ b/src/components/NftCollectionTransfers/index.tsx @@ -5,15 +5,16 @@ import { useQuery } from 'react-query' import { Tooltip } from 'antd' import { Base64 } from 'js-base64' import { hexToBytes } from '@nervosnetwork/ckb-sdk-utils' +import { useTranslation } from 'react-i18next' import { parseSporeCellData } from '../../utils/spore' import type { TransferListRes, TransferRes } from '../../pages/NftCollectionInfo' -import i18n from '../../utils/i18n' import styles from './styles.module.scss' import { getPrimaryColor } from '../../constants/common' import { handleNftImgError, patchMibaoImg } from '../../utils/util' import { explorerService } from '../../services/ExplorerService' import { dayjs } from '../../utils/date' import { useParsedDate, useTimestamp } from '../../utils/hook' +import { useCurrentLanguage } from '../../utils/i18n' const primaryColor = getPrimaryColor() @@ -42,31 +43,32 @@ NftCollectionTransfers.displayName = 'NftTransfers' const TransferTable: FC = ({ collection, iconURL, list, isLoading }) => { const [isShowInAge, setIsShowInAge] = useState(false) - - dayjs.locale(i18n.language === 'zh' ? 'zh-cn' : 'en') + const { t } = useTranslation() + const currentLanguage = useCurrentLanguage() + dayjs.locale(currentLanguage === 'zh' ? 'zh-cn' : 'en') return ( - - - + + + - - + + @@ -83,7 +85,7 @@ const TransferTable: FC = ({ collection, iconURL, list, ) : ( )} @@ -98,6 +100,7 @@ const TransferTableRow: FC<{ iconURL?: string | null isShowInAge?: boolean }> = ({ collection, item, iconURL, isShowInAge }) => { + const { t } = useTranslation() const coverUrl = item.item.icon_url ?? iconURL const parsedBlockCreateAt = useParsedDate(item.transaction.block_timestamp) const now = useTimestamp() @@ -171,7 +174,7 @@ const TransferTableRow: FC<{ - +
{i18n.t('nft.nft')}{i18n.t('nft.tx_hash')}{i18n.t('nft.action')}{t('nft.nft')}{t('nft.tx_hash')}{t('nft.action')} setIsShowInAge(show => !show)} className={styles.age} - title={i18n.t('nft.toggle-age')} + title={t('nft.toggle-age')} style={{ color: primaryColor, }} > - {i18n.t('nft.age')} + {t('nft.age')} {i18n.t('nft.from')}{i18n.t('nft.to')}{t('nft.from')}{t('nft.to')}
- {isLoading ? i18n.t('nft.loading') : i18n.t(`nft.no_record`)} + {isLoading ? t('nft.loading') : t(`nft.no_record`)}
{i18n.t(`nft.action_type.${item.action}`)}{t(`nft.action_type.${item.action}`)} {isShowInAge ? timeRelativeBlockCreate : parsedBlockCreateAt} {item.from ? ( @@ -212,12 +215,13 @@ const TransferTableRow: FC<{ } const TransferCardGroup: FC = ({ collection, iconURL, list, isLoading }) => { + const { t } = useTranslation() return (
    {list.length ? ( list.map(item => ) ) : ( -
  • {isLoading ? i18n.t('nft.loading') : i18n.t(`nft.no_record`)}
  • +
  • {isLoading ? t('nft.loading') : t(`nft.no_record`)}
  • )}
) @@ -228,6 +232,7 @@ const TransferCard: FC<{ item: TransferRes iconURL?: string | null }> = ({ collection, item, iconURL }) => { + const { t } = useTranslation() const coverUrl = item.item.icon_url ?? iconURL const parsedBlockCreateAt = useParsedDate(item.transaction.block_timestamp) @@ -284,7 +289,7 @@ const TransferCard: FC<{
-
{i18n.t('nft.tx_hash')}
+
{t('nft.tx_hash')}
-
{i18n.t('nft.action')}
-
{i18n.t(`nft.action_type.${item.action}`)}
+
{t('nft.action')}
+
{t(`nft.action_type.${item.action}`)}
-
{i18n.t('nft.age')}
+
{t('nft.age')}
{parsedBlockCreateAt}
-
{i18n.t('nft.from')}
+
{t('nft.from')}
{item.from ? (
-
{i18n.t('nft.to')}
+
{t('nft.to')}
{item.to ? ( isLoading: boolean }> = ({ list, isLoading }) => { + const { t } = useTranslation() + return (
- + @@ -49,7 +51,7 @@ const NftHolderList: React.FC<{ ) : ( )} diff --git a/src/components/NftItemTransfers/index.tsx b/src/components/NftItemTransfers/index.tsx index ef9507df1..b5aca7fde 100644 --- a/src/components/NftItemTransfers/index.tsx +++ b/src/components/NftItemTransfers/index.tsx @@ -1,10 +1,11 @@ import { useState } from 'react' import { Link } from 'react-router-dom' import { Tooltip } from 'antd' -import i18n from '../../utils/i18n' +import { useTranslation } from 'react-i18next' import { getPrimaryColor } from '../../constants/common' -import { dayjs, parseDate } from '../../utils/date' +import { dayjs, useParseDate } from '../../utils/date' import styles from './styles.module.scss' +import { useCurrentLanguage } from '../../utils/i18n' const primaryColor = getPrimaryColor() @@ -40,30 +41,34 @@ export interface TransferListRes { } const NftItemTransfers: React.FC<{ list: TransferListRes['data']; isLoading: boolean }> = ({ list, isLoading }) => { const [isShowInAge, setIsShowInAge] = useState(false) - dayjs.locale(i18n.language === 'zh' ? 'zh-cn' : 'en') + const { t } = useTranslation() + const parseDate = useParseDate() + const currentLanguage = useCurrentLanguage() + + dayjs.locale(currentLanguage === 'zh' ? 'zh-cn' : 'en') return (
{i18n.t('nft.holder')}{t('nft.holder')} - {i18n.t('nft.quantity')} + {t('nft.quantity')}
- {isLoading ? i18n.t('nft.loading') : i18n.t(`nft.no_record`)} + {isLoading ? t('nft.loading') : t(`nft.no_record`)}
- - + + - - + + @@ -87,7 +92,7 @@ const NftItemTransfers: React.FC<{ list: TransferListRes['data']; isLoading: boo - + )} @@ -145,7 +150,7 @@ const NftItemTransfers: React.FC<{ list: TransferListRes['data']; isLoading: boo
  • -
    {i18n.t('nft.tx_hash')}
    +
    {t('nft.tx_hash')}
    -
    {i18n.t('nft.action')}
    -
    {i18n.t(`nft.action_type.${item.action}`)}
    +
    {t('nft.action')}
    +
    {t(`nft.action_type.${item.action}`)}
    -
    {i18n.t('nft.age')}
    +
    {t('nft.age')}
    {parseDate(item.transaction.block_timestamp)}
    -
    {i18n.t('nft.from')}
    +
    {t('nft.from')}
    {item.from ? (
    -
    {i18n.t('nft.to')}
    +
    {t('nft.to')}
    {item.to ? ( )) ) : ( -
  • {isLoading ? i18n.t('nft.loading') : i18n.t(`nft.no_record`)}
  • +
  • {isLoading ? t('nft.loading') : t(`nft.no_record`)}
  • )}
    diff --git a/src/components/Pagination/index.tsx b/src/components/Pagination/index.tsx index 9bd1a37ea..cb6e5ae71 100644 --- a/src/components/Pagination/index.tsx +++ b/src/components/Pagination/index.tsx @@ -1,10 +1,10 @@ import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { PaginationLeftItem, PaginationRightItem, PaginationPanel } from './styled' import LeftBlack from '../../assets/pagination_black_left.png' import RightBlack from '../../assets/pagination_black_right.png' import LeftGrey from '../../assets/pagination_grey_left.png' import RightGrey from '../../assets/pagination_grey_right.png' -import i18n from '../../utils/i18n' import { useIsMobile } from '../../utils/hook' import SimpleButton from '../SimpleButton' import { HelpTip } from '../HelpTip' @@ -25,15 +25,16 @@ const Pagination = ({ annotation?: string }) => { const isMobile = useIsMobile() + const { t } = useTranslation() const [inputPage, setInputPage] = useState(gotoPage) const total = Math.max(totalPages, 1) const current = Math.min(Math.max(currentPage, 1), totalPages) - const mobilePagination = `${i18n.t('pagination.total_page')} ${total} ${i18n.t('pagination.end_page')}` - const pcPagination = `${i18n.t('pagination.current_page')} ${current} ${i18n.t( - 'pagination.of_page', - )} ${total} ${i18n.t('pagination.end_page')}` + const mobilePagination = `${t('pagination.total_page')} ${total} ${t('pagination.end_page')}` + const pcPagination = `${t('pagination.current_page')} ${current} ${t('pagination.of_page')} ${total} ${t( + 'pagination.end_page', + )}` const annotationComp = annotation ? : null @@ -48,7 +49,7 @@ const Pagination = ({ changePage(1)}> - {i18n.t('pagination.first')} + {t('pagination.first')} changePage(current - 1)}> left button @@ -71,11 +72,11 @@ const Pagination = ({ )} changePage(total)}> - {i18n.t('pagination.last')} + {t('pagination.last')} - {i18n.t('pagination.page')} + {t('pagination.page')} changePage(inputPage)}> - {i18n.t('pagination.goto')} + {t('pagination.goto')} diff --git a/src/components/Pagination/styled.tsx b/src/components/Pagination/styled.tsx index 11f204aca..e08de2274 100644 --- a/src/components/Pagination/styled.tsx +++ b/src/components/Pagination/styled.tsx @@ -9,10 +9,6 @@ export const PaginationPanel = styled.div` justify-content: center; border-radius: 0 0 6px 6px; box-shadow: 0 2px 6px 0 rgb(0 0 0 / 12%); - - @media (max-width: 750px) { - margin-bottom: 30px; - } ` export const PaginationLeftItem = styled.div` @@ -205,6 +201,7 @@ export const PaginationRightItem = styled.div` @media (max-width: 750px) { margin-left: 10px; + margin-right: 10px; font-size: 12px; } } diff --git a/src/components/Script/index.tsx b/src/components/Script/index.tsx index c36762906..89ccd07ec 100644 --- a/src/components/Script/index.tsx +++ b/src/components/Script/index.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react' +import { useTranslation } from 'react-i18next' import { ScriptItemPanel, ScriptPanel } from './styled' -import i18n from '../../utils/i18n' import HashTag from '../HashTag' import { getContractHashTag } from '../../utils/util' import { HelpTip } from '../HelpTip' @@ -18,18 +18,20 @@ const ScriptItem = ({ title, tooltip, children }: { title: string; tooltip?: str const Script = ({ script }: { script: State.Script }) => { const contractHashTag = getContractHashTag(script) + const { t } = useTranslation() + return ( - +
    {script.codeHash} {contractHashTag && }
    - + {script.hashType} - + {script.args}
    diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 01f6778d0..cdc3a39d7 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,13 +1,12 @@ -import { useState, useRef, useEffect, useMemo, FC, memo } from 'react' +import { useState, useRef, useEffect, FC, memo } from 'react' import { useHistory } from 'react-router' import { AxiosError } from 'axios' -import { useTranslation } from 'react-i18next' +import { TFunction, useTranslation } from 'react-i18next' import { SearchImage, SearchInputPanel, SearchPanel, SearchButton, SearchContainer } from './styled' import { explorerService, Response } from '../../services/ExplorerService' import SearchLogo from '../../assets/search_black.png' import ClearLogo from '../../assets/clear.png' import { addPrefixForHash, containSpecialChar } from '../../utils/string' -import i18n from '../../utils/i18n' import { HttpErrorCode, SearchFailType } from '../../constants/common' import { useIsMobile } from '../../utils/hook' import { isChainTypeError } from '../../utils/chain' @@ -28,9 +27,9 @@ const clearSearchInput = (inputElement: any) => { } } -const setSearchLoading = (inputElement: any) => { +const setSearchLoading = (inputElement: any, t: TFunction) => { const input: HTMLInputElement = inputElement.current - input.value = i18n.t('search.loading') + input.value = t('search.loading') } const setSearchContent = (inputElement: any, content: string) => { @@ -45,6 +44,7 @@ const handleSearchResult = ( inputElement: any, setSearchValue: Function, history: ReturnType, + t: TFunction, ) => { const query = searchValue.trim().replace(',', '') // remove front and end blank and ',' if (!query || containSpecialChar(query)) { @@ -56,7 +56,7 @@ const handleSearchResult = ( return } - setSearchLoading(inputElement) + setSearchLoading(inputElement, t) explorerService.api .fetchSearchResult(addPrefixForHash(query)) .then((response: any) => { @@ -103,9 +103,9 @@ const Search: FC<{ onEditEnd?: () => void }> = memo(({ content, hasButton, onEditEnd }) => { const isMobile = useIsMobile() + const { t } = useTranslation() const history = useHistory() - const [t] = useTranslation() - const SearchPlaceholder = useMemo(() => t('navbar.search_placeholder'), [t]) + const SearchPlaceholder = t('navbar.search_placeholder') const [searchValue, setSearchValue] = useState(content || '') const [placeholder, setPlaceholder] = useState(SearchPlaceholder) const inputElement = useRef(null) @@ -138,7 +138,7 @@ const Search: FC<{ const searchKeyAction = (event: any) => { if (event.keyCode === 13) { - handleSearchResult(searchValue, inputElement, setSearchValue, history) + handleSearchResult(searchValue, inputElement, setSearchValue, history, t) onEditEnd?.() } } @@ -165,11 +165,11 @@ const Search: FC<{ {hasButton && ( { - handleSearchResult(searchValue, inputElement, setSearchValue, history) + handleSearchResult(searchValue, inputElement, setSearchValue, history, t) onEditEnd?.() }} > - {i18n.t('search.search')} + {t('search.search')} )} diff --git a/src/components/Sheet/index.tsx b/src/components/Sheet/index.tsx index 0cb700946..ef24153dd 100644 --- a/src/components/Sheet/index.tsx +++ b/src/components/Sheet/index.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from 'react-i18next' import { SheetPanel, SheetPointPanel, SheetItem } from './styled' import { createGlobalState, createGlobalStateSetter, useGlobalState } from '../../utils/state' @@ -8,6 +9,7 @@ export const setNetworkErrMsgs = createGlobalStateSetter(globalNetworkErrMsgs) export const setChainAlerts = createGlobalStateSetter(globalChainAlerts) const Sheet = () => { + const { t } = useTranslation() const [networkErrMsgs] = useGlobalState(globalNetworkErrMsgs) const [chainAlerts] = useGlobalState(globalChainAlerts) const messages: string[] = chainAlerts.concat(networkErrMsgs) @@ -19,7 +21,7 @@ const Sheet = () => { return ( {messages.length > 1 && ยท} - {context} + {t(context)} ) })} diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index 259f79c6c..67b5e31a1 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -1,6 +1,6 @@ import { memo, ReactNode } from 'react' import { Col, Row } from 'antd' -import i18n from '../../utils/i18n' +import { useTranslation } from 'react-i18next' import { TableTitleRowItem, TableContentRowItem, HighlightLink, TableMinerContentPanel } from './styled' import AddressText from '../AddressText' @@ -31,6 +31,7 @@ export const TableMinerContentItem = memo( textCenter?: boolean fontSize?: string }) => { + const { t } = useTranslation() return ( {content ? ( @@ -48,7 +49,7 @@ export const TableMinerContentItem = memo( ) : ( -
    {i18n.t('address.unable_decode_address')}
    +
    {t('address.unable_decode_address')}
    )}
    ) diff --git a/src/components/Text/CopyTooltipText/index.tsx b/src/components/Text/CopyTooltipText/index.tsx index 7582fc369..5e970be81 100644 --- a/src/components/Text/CopyTooltipText/index.tsx +++ b/src/components/Text/CopyTooltipText/index.tsx @@ -1,17 +1,19 @@ -import i18n from '../../../utils/i18n' +import { useTranslation } from 'react-i18next' import { copyElementValue } from '../../../utils/util' import SimpleButton from '../../SimpleButton' import { useSetToast } from '../../Toast' export default ({ content }: { content: string }) => { const setToast = useSetToast() + const { t } = useTranslation() + return ( { event.stopPropagation() copyElementValue(document.getElementById(`copy__content__${content}`)) - setToast({ message: i18n.t('common.copied') }) + setToast({ message: t('common.copied') }) event.preventDefault() }} > diff --git a/src/components/Transaction/TransactionCellArrow/index.tsx b/src/components/Transaction/TransactionCellArrow/index.tsx index 8fa6ceb26..4b5ebf3e4 100644 --- a/src/components/Transaction/TransactionCellArrow/index.tsx +++ b/src/components/Transaction/TransactionCellArrow/index.tsx @@ -1,12 +1,12 @@ import { Link } from 'react-router-dom' import { Tooltip } from 'antd' +import { useTranslation } from 'react-i18next' import { CellType } from '../../../constants/common' import RightGreenArrow from '../../../assets/right_green_arrow.png' import RightBlueArrow from '../../../assets/right_blue_arrow.png' import LiveCellIcon from '../../../assets/live_cell.png' import LiveCellBlueIcon from '../../../assets/live_cell_blue.png' import { isMainnet } from '../../../utils/chain' -import i18n from '../../../utils/i18n' import { RightArrowImage, LeftArrowImage } from './styled' const CellInputIcon = ({ cell }: { cell: State.Cell }) => @@ -21,6 +21,8 @@ const CellInputIcon = ({ cell }: { cell: State.Cell }) => ) : null const CellOutputIcon = ({ cell }: { cell: State.Cell }) => { + const { t } = useTranslation() + if (cell.status === 'dead') { return ( @@ -29,7 +31,7 @@ const CellOutputIcon = ({ cell }: { cell: State.Cell }) => { ) } return ( - + ) diff --git a/src/components/TransactionItem/TransactionIncome/index.tsx b/src/components/TransactionItem/TransactionIncome/index.tsx index 9c413bc0e..eba38aa33 100644 --- a/src/components/TransactionItem/TransactionIncome/index.tsx +++ b/src/components/TransactionItem/TransactionIncome/index.tsx @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' import { Tooltip } from 'antd' -import i18n from '../../../utils/i18n' +import { useTranslation } from 'react-i18next' import { TransactionIncomePanel, TransactionCapacityValuePanel } from './styled' import { shannonToCkb } from '../../../utils/util' import { localeNumberString } from '../../../utils/number' @@ -10,6 +10,7 @@ import CurrentAddressIcon from '../../../assets/current_address.svg' export default ({ income }: { income: string }) => { const isMobile = useIsMobile() + const { t } = useTranslation() let bigIncome = new BigNumber(income) if (bigIncome.isNaN()) { bigIncome = new BigNumber(0) @@ -19,7 +20,7 @@ export default ({ income }: { income: string }) => { {isMobile && ( - + current Address )} @@ -28,7 +29,7 @@ export default ({ income }: { income: string }) => { balanceChangeType={isIncome ? 'income' : 'payment'} /> {!isMobile && ( - + current Address )} diff --git a/src/components/TransactionItem/TransactionItemCell/index.tsx b/src/components/TransactionItem/TransactionItemCell/index.tsx index dabd20c7f..0e1a11cf1 100644 --- a/src/components/TransactionItem/TransactionItemCell/index.tsx +++ b/src/components/TransactionItem/TransactionItemCell/index.tsx @@ -2,11 +2,12 @@ import { FC, ReactNode } from 'react' import { Link } from 'react-router-dom' import { Popover, Tooltip } from 'antd' import classNames from 'classnames' +import { useTranslation } from 'react-i18next' import NervosDAOCellIcon from '../../../assets/nervos_dao_cell.png' import NervosDAOWithdrawingIcon from '../../../assets/nervos_dao_withdrawing.png' import CurrentAddressIcon from '../../../assets/current_address.svg' import UDTTokenIcon from '../../../assets/udt_token.png' -import i18n, { currentLanguage } from '../../../utils/i18n' +import { useCurrentLanguage } from '../../../utils/i18n' import { localeNumberString, parseUDTAmount } from '../../../utils/number' import { isDaoCell, isDaoDepositCell, isDaoWithdrawCell, shannonToCkb, shannonToCkbDecimal } from '../../../utils/util' import { @@ -64,10 +65,12 @@ const AddressTextWithAlias: FC<{ ) } -const udtAmount = (udt: State.UDTInfo) => - udt.published +const useUdtAmount = (udt: State.UDTInfo) => { + const { t } = useTranslation() + return udt.published ? `${parseUDTAmount(udt.amount, udt.decimal)} ${udt.uan || udt.symbol}` - : `${i18n.t('udt.unknown_token')} #${udt.typeHash.substring(udt.typeHash.length - 4)}` + : `${t('udt.unknown_token')} #${udt.typeHash.substring(udt.typeHash.length - 4)}` +} const WithdrawPopoverItem = ({ width, @@ -86,27 +89,27 @@ const WithdrawPopoverItem = ({ const WithdrawPopoverInfo = ({ cell }: { cell: State.Cell }) => { const isMobile = useIsMobile() + const { t } = useTranslation() + const currentLanguage = useCurrentLanguage() let width = 'short' - if (currentLanguage() === 'en') { + if (currentLanguage === 'en') { width = isDaoDepositCell(cell.cellType) ? 'long' : 'medium' } return (

    - {isDaoWithdrawCell(cell.cellType) - ? i18n.t('nervos_dao.withdraw_tooltip') - : i18n.t('nervos_dao.withdraw_request_tooltip')} + {isDaoWithdrawCell(cell.cellType) ? t('nervos_dao.withdraw_tooltip') : t('nervos_dao.withdraw_request_tooltip')}

    } /> { /> - {`${i18n.t('block.block')} `} + {`${t('block.block')} `} {localeNumberString(cell.compensationStartedBlockNumber)} @@ -131,17 +134,17 @@ const WithdrawPopoverInfo = ({ cell }: { cell: State.Cell }) => { /> {isDaoWithdrawCell(cell.cellType) && ( <> - {`${i18n.t('block.block')} `} + {`${t('block.block')} `} {localeNumberString(cell.compensationStartedBlockNumber)} @@ -154,7 +157,7 @@ const WithdrawPopoverInfo = ({ cell }: { cell: State.Cell }) => { /> @@ -165,6 +168,7 @@ const WithdrawPopoverInfo = ({ cell }: { cell: State.Cell }) => { const TransactionCellNervosDao = ({ cell, cellType }: { cell: State.Cell; cellType: CellType }) => { const isMobile = useIsMobile() + const { t } = useTranslation() return ( @@ -175,9 +179,7 @@ const TransactionCellNervosDao = ({ cell, cellType }: { cell: State.Cell; cellTy ) : ( { return ( - {udtAmount(extraInfo)} + {useUdtAmount(extraInfo)} { const isMobile = useIsMobile() + const { t } = useTranslation() if (cell.fromCellbase) { return } - let addressText = i18n.t('address.unable_decode_address') + let addressText = t('address.unable_decode_address') let highLight = false if (cell.addressHash) { addressText = cell.addressHash @@ -247,14 +250,14 @@ const TransactionCell = ({ cell, address, cellType }: { cell: State.Cell; addres {cellType === CellType.Output && } {!highLight && !isMobile && ( - + current Address )} {!highLight && isMobile && ( - + current Address )} diff --git a/src/components/TransactionItem/TransactionItemCellList/index.tsx b/src/components/TransactionItem/TransactionItemCellList/index.tsx index af441df2e..5d2ed3d8f 100644 --- a/src/components/TransactionItem/TransactionItemCellList/index.tsx +++ b/src/components/TransactionItem/TransactionItemCellList/index.tsx @@ -1,7 +1,7 @@ import { ReactNode } from 'react' import { Link } from 'react-router-dom' +import { useTranslation } from 'react-i18next' import TransactionCellListPanel from './styled' -import i18n from '../../../utils/i18n' const MAX_CELL_SHOW_SIZE = 10 @@ -13,13 +13,16 @@ export default ({ cells: State.Cell[] transaction: State.Transaction render: (cell: State.Cell) => ReactNode -}) => ( - - {cells && cells.map((cell, index) => index < MAX_CELL_SHOW_SIZE && render(cell))} - {cells && cells.length >= MAX_CELL_SHOW_SIZE && ( -
    - {i18n.t('common.view_all')} -
    - )} -
    -) +}) => { + const { t } = useTranslation() + return ( + + {cells && cells.map((cell, index) => index < MAX_CELL_SHOW_SIZE && render(cell))} + {cells && cells.length >= MAX_CELL_SHOW_SIZE && ( +
    + {t('common.view_all')} +
    + )} +
    + ) +} diff --git a/src/components/TransactionItem/TransactionLiteItem/index.tsx b/src/components/TransactionItem/TransactionLiteItem/index.tsx index a49f49c81..a0a8439f8 100644 --- a/src/components/TransactionItem/TransactionLiteItem/index.tsx +++ b/src/components/TransactionItem/TransactionLiteItem/index.tsx @@ -1,19 +1,20 @@ import { Link } from 'react-router-dom' +import { useTranslation } from 'react-i18next' import { localeNumberString } from '../../../utils/number' import AddressText from '../../AddressText' import styles from './index.module.scss' import TransactionLiteIncome from '../TransactionLiteIncome' -import i18n from '../../../utils/i18n' import { useIsMobile, useParsedDate } from '../../../utils/hook' const TransactionLiteItem = ({ transaction, address }: { transaction: State.Transaction; address?: string }) => { const isMobile = useIsMobile() + const { t } = useTranslation() const parsedBlockCreateAt = useParsedDate(transaction.blockTimestamp) return (
    - {isMobile &&
    {i18n.t('transaction.transaction_hash')}
    } + {isMobile &&
    {t('transaction.transaction_hash')}
    }
    - {isMobile &&
    {i18n.t('transaction.height')}
    } + {isMobile &&
    {t('transaction.height')}
    } {localeNumberString(transaction.blockNumber)}
    - {isMobile &&
    {i18n.t('transaction.time')}
    } + {isMobile &&
    {t('transaction.time')}
    } {parsedBlockCreateAt}
    - {isMobile &&
    {`${i18n.t('transaction.input')} & ${i18n.t('transaction.output')}`}
    } + {isMobile &&
    {`${t('transaction.input')} & ${t('transaction.output')}`}
    } + {transaction.displayInputs && `${t('transaction.input')}: ${transaction.displayInputs.length}`} - {transaction.displayInputs && `${i18n.t('transaction.input')}: ${transaction.displayInputs.length}`} - - - {transaction.displayOutputs && `${i18n.t('transaction.output')}: ${transaction.displayOutputs.length}`} + {transaction.displayOutputs && `${t('transaction.output')}: ${transaction.displayOutputs.length}`}
    - {isMobile &&
    {i18n.t('transaction.capacity_change')}
    } + {isMobile &&
    {t('transaction.capacity_change')}
    } {address && }
    diff --git a/src/components/TransactionItem/index.tsx b/src/components/TransactionItem/index.tsx index bebf31670..1ff620794 100644 --- a/src/components/TransactionItem/index.tsx +++ b/src/components/TransactionItem/index.tsx @@ -1,4 +1,5 @@ import { ReactNode, useEffect, useRef } from 'react' +import { useTranslation } from 'react-i18next' import RightArrowIcon from '../../assets/input_arrow_output.png' import DownArrowIcon from '../../assets/input_arrow_output_down.png' import { localeNumberString } from '../../utils/number' @@ -6,7 +7,6 @@ import TransactionCell from './TransactionItemCell' import TransactionCellList from './TransactionItemCellList' import TransactionIncome from './TransactionIncome' import { FullPanel, TransactionHashBlockPanel, TransactionCellPanel, TransactionPanel } from './styled' -import i18n from '../../utils/i18n' import { CellType } from '../../constants/common' import AddressText from '../AddressText' import { useIsLGScreen, useParsedDate } from '../../utils/hook' @@ -35,6 +35,7 @@ const TransactionItem = ({ scrollIntoViewOnMount?: boolean }) => { const isLG = useIsLGScreen() + const { t } = useTranslation() const ref = useRef(null) useEffect(() => { @@ -67,7 +68,7 @@ const TransactionItem = ({ {!isBlock && (
    - {`(${i18n.t('block.block')} ${localeNumberString(transaction.blockNumber)}) ${parsedBlockCreateAt}`} + {`(${t('block.block')} ${localeNumberString(transaction.blockNumber)}) ${parsedBlockCreateAt}`}
    )}
    @@ -93,7 +94,7 @@ const TransactionItem = ({ )} /> ) : ( -
    {i18n.t('transaction.empty_output')}
    +
    {t('transaction.empty_output')}
    )} diff --git a/src/constants/common.ts b/src/constants/common.ts index 9d0d5209f..c6bc2792b 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -13,6 +13,8 @@ export const EPOCH_HOURS = 4 export const ONE_DAY_SECOND = 24 * 60 * 60 export const ONE_HOUR_SECOND = 60 * 60 export const ONE_MINUTE_SECOND = 60 +export const EPOCHS_PER_HALVING = 8760 +export const THEORETICAL_EPOCH_TIME = 1000 * 60 * 60 * 4 // 4 hours export const IS_MAINTAINING = process.env.REACT_APP_IS_MAINTAINING === 'true' export function getPrimaryColor() { diff --git a/src/contexts/providers/hook.ts b/src/contexts/providers/hook.ts index 3588c9ef6..24552a4a1 100644 --- a/src/contexts/providers/hook.ts +++ b/src/contexts/providers/hook.ts @@ -1,24 +1,13 @@ import { useState } from 'react' import { FLUSH_CHART_CACHE_POLLING_TIME } from '../../constants/common' -import { AppCachedKeys } from '../../constants/cache' -import { fetchCachedData } from '../../utils/cache' -import { changeLanguage } from '../../utils/i18n' import { useInterval } from '../../utils/hook' import flushCacheInfo from '../../service/app/charts/cache' -const initAppLanguage = () => { - const language = - fetchCachedData<'zh' | 'en'>(AppCachedKeys.AppLanguage) || (navigator.language.includes('zh') ? 'zh' : 'en') - changeLanguage(language) -} - export const useInitApp = () => { const [init, setInit] = useState(false) if (!init) { setInit(true) - // TODO: This function may not belong here. - initAppLanguage() flushCacheInfo() } diff --git a/src/locales/en.json b/src/locales/en.json index 10df76ea6..46c58145f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -19,6 +19,83 @@ "minaraTime": "Est May 10th 2022", "miranaAlive": "MIRANA IS LIVE" }, + "unit": { + "days": "days", + "hours": "hours", + "minutes": "minutes", + "seconds": "seconds" + }, + "symbol": { + "char_space": " " + }, + "ordinal": { + "first": "first", + "second": "second", + "1st": "1st", + "2nd": "2nd", + "3rd": "3rd", + "4th": "4th", + "5th": "5th", + "6th": "6th", + "7th": "7th", + "8th": "8th", + "9th": "9th", + "10th": "10th" + }, + "halving": { + "congratulations": "Congratulations", + "the": "the", + "actived": "is atived on block", + "halving": "halving", + "next": "next", + "and": "and", + "comming_soon": "Halving comes in seconds", + "halving_countdown": "Halving Countdown", + "learn_more": "Learn More", + "banner_congratulation": "The {{times}} CKB halving is active", + "countdown_tooltip_section1": "How is the countdown estimated?", + "countdown_tooltip_section2": "[Target Epoch - Current Epoch]*Single Epoch Average Time - Current Epoch Used Time", + "countdown_tooltip_section3": "*Single Epoch Average Time takes the average time of nearly 1000 epochs.", + "halving_desc_prefix": "will have a halving event every 4 years according to the mechanism, at which time the", + "base_issuance_rewards": "base issuance rewards", + "halving_desc_suffix": "will be halved.", + "current_block": "Current Block", + "current_epoch": "Current Epoch", + "target_epoch": "Target Epoch", + "estimated_time": "Estimated Time", + "share_tooltip": "Share CKB Halving Event to X", + "halving_event": "What is a Halving Event?", + "halving_event_section_1": "In the Nervos ecosystem, mining is used to secure the network and distribute tokens in the form of block rewards. A total of 33.6 billion CKB tokens will be created through primary issuance over a period of approximately 84 years to incentivize the miners that secure the network.", + "halving_event_section_2": "Every epoch, a period of approximately four hours, a fixed amount of 1,917,808 CKB is introduced. Every 8,760 epochs, a period of approximately four years, this amount is cut in half. This event is called a halving and it is the point where the mining rewards from primary issuance are permanently reduced by 50%. This halving process will continue every four years until the year 2103, after which point all block rewards from primary issuance will cease completely.", + "significance": "What is the Significance of a Halving?", + "significance_section_1": "Each time a halving occurs, it causes a sharp decrease in the rewards generated per block. The supply of new CKB entering circulation is lowered, dramatically reducing the rate of inflation. This is important because it creates a shift in the underlying market equilibrium and forces a reevaluation of what is considered fair market value.", + "significance_section_2": "Halving events occur on a predetermined issuance schedule that cannot be changed, postponed, or delayed. Investors and community members often look forward to a halving event as something to celebrate since it marks an important milestone in the history of the project.", + "how_does_work": "How does the CKByte-Halving work?", + "how_does_work_section_1": "In order to make the halving plan work as expected, a concept of time called epoch was introduced.", + "how_does_work_section_2": "An epoch is a period of time for a set of blocks.", + "how_does_work_section_3": "In Nervos, the PoW difficulty changes on a new epoch. All the blocks in the same epoch share the same difficulty target. The difficulty adjustment algorithm aims to stabilize the orphan block rate at 2.5% and the epoch duration at 4 hours.", + "how_does_work_section_4": "Epochs per halving is", + "how_does_work_section_5": "and the Nth halving of CKBytes firstly occurs on epoch", + "how_does_work_section_6": "So, The CKByte halving event occurs on the specified epoch, e.g. 8760, 17520.", + "when": "When will CKByte be halved?", + "when_section_1": "The following table details the schedule for several upcoming CKB halvings and their corresponding base issuance rewards:", + "when_section_2": "Note that CKB block rewards include ", + "when_section_3": "Base (issuance) reward, Secondary (issuance) reward, Commit reward", + "when_section_4": "Proposal reward", + "when_section_5": "However, when we are discussing CKB halving, it only relates to ", + "when_section_6": ". Therefore, the block rewards listed in the table only include the portion of rewards from base issuance to help with understanding.", + "table_event": "Event", + "table_date": "Date", + "table_epoch_number": "Epoch number", + "table_epoch_reward": "Epoch reward", + "table_block_reward": "Block reward(Calculated based on 1800 blocks per epoch)", + "table_daily_reward": "Daily reward", + "table_total": "Total new CKB between events", + "table_launches": "Nervos launches", + "genesis_epoch": "genesis epoch", + "expected": "Expected", + "share_text": "๐Ÿ“ข @NervosNetwork CKB Expected {{times}} halving on {{date}}๐Ÿš€๏ผŒ\n\n๐Ÿ“…There are {{countdown}} left in the countdown to the halving.\n\n๐Ÿ”— Click ๐Ÿ‘‰ https://explorer.nervos.org know more" + }, "glossary": { "block_height": "Also known as Block Number. The block height, which indicates the length of the blockchain, increases after the addition of the new block.", "cell": "CKB adopts cell model, which is more generic than UTXO model. A transaction destroys some outputs created in previous transactions and creates some new outputs. Every transaction's output is a cell.", diff --git a/src/locales/zh.json b/src/locales/zh.json index b723d8fae..55bfe6acc 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -19,6 +19,83 @@ "minaraTime": "็พŽๅ›ฝไธœ้ƒจๆ—ถ้—ด: 2022 ๅนด 05 ๆœˆ 10 ๆ—ฅ", "miranaAlive": "MIRANA ๅทฒ็ปๆฟ€ๆดป" }, + "unit": { + "days": "ๅคฉ", + "hours": "ๅฐๆ—ถ", + "minutes": "ๅˆ†้’Ÿ", + "seconds": "็ง’" + }, + "symbol": { + "char_space": "" + }, + "ordinal": { + "first": "้ฆ–ๆฌก", + "second": "็ฌฌ 2 ๆฌก", + "1st": "้ฆ–ๆฌก", + "2nd": "็ฌฌ 2 ๆฌก", + "3rd": "็ฌฌ 3 ๆฌก", + "4th": "็ฌฌ 4 ๆฌก", + "5th": "็ฌฌ 5 ๆฌก", + "6th": "็ฌฌ 6 ๆฌก", + "7th": "็ฌฌ 7 ๆฌก", + "8th": "็ฌฌ 8 ๆฌก", + "9th": "็ฌฌ 9 ๆฌก", + "10th": "็ฌฌ 10 ๆฌก" + }, + "halving": { + "congratulations": "ๆญๅ–œ", + "the": "", + "actived": "็”Ÿๆ•ˆไบŽ้ซ˜ๅบฆ", + "next": "ไธ‹ไธ€ๆฌก", + "halving": "ๅ‡ๅŠ", + "and": "ๅ’Œ", + "comming_soon": "ๅ‡ๅŠๅฐ†ๅœจๅ‡ ็ง’้’Ÿๅ†…ๅ‘็”Ÿ", + "halving_countdown": "ๅ‡ๅŠๅ€’่ฎกๆ—ถ", + "learn_more": "ไบ†่งฃๆ›ดๅคš", + "banner_congratulation": "CKB {{times}}ๅ‡ๅŠ็”Ÿๆ•ˆ", + "countdown_tooltip_section1": "ๅ€’่ฎกๆ—ถๆ˜ฏๅฆ‚ไฝ•ไผฐ็ฎ—็š„?", + "countdown_tooltip_section2": "[็›ฎๆ ‡ Epoch - ๅฝ“ๅ‰ Epoch] * ๅ•ไธช Epoch ๅนณๅ‡็”จๆ—ถ - ๅฝ“ๅ‰ Epoch ๅทฒ็”จๆ—ถ้—ด", + "countdown_tooltip_section3": "*ๅ•ไธช Epoch ๅนณๅ‡็”จๆ—ถๅ–่ฟ‘1000ไธช Epoch ็š„ๅนณๅ‡็”จๆ—ถ.", + "halving_desc_prefix": "ๆ นๆฎๆœบๅˆถๅฐ†ๆฏ4ๅนดๅ‘็”Ÿไธ€ๆฌกๅ‡ๅŠไบ‹ไปถ๏ผŒๅฑŠๆ—ถ", + "base_issuance_rewards": "ๅŸบ็ก€ๅ‘่กŒๅฅ–ๅŠฑ", + "halving_desc_suffix": "ๅฐ†ๅ‡ๅŠใ€‚", + "current_block": "ๅฝ“ๅ‰ๅŒบๅ—", + "current_epoch": "ๅฝ“ๅ‰ Epoch", + "target_epoch": "็›ฎๆ ‡ Epoch", + "estimated_time": "้ข„่ฎกๆ—ถ้—ด", + "share_tooltip": "ๅˆ†ไบซ CKB ๅ‡ๅŠไบ‹ไปถๅˆฐ X", + "halving_event": "ไป€ไนˆๆ˜ฏๅ‡ๅŠไบ‹ไปถ?", + "halving_event_section_1": "ๅœจ Nervos ็”Ÿๆ€็ณป็ปŸไธญ๏ผŒๆŒ–็Ÿฟ็”จไบŽ็กฎไฟ็ฝ‘็ปœๅฎ‰ๅ…จ๏ผŒๅนถไปฅๅŒบๅ—ๅฅ–ๅŠฑ็š„ๅฝขๅผๅˆ†้…ไปฃๅธใ€‚ๅœจๅคง็บฆ 84 ๅนด็š„ๆ—ถ้—ด้‡Œ๏ผŒๅฐ†้€š่ฟ‡้ฆ–ๆฌกๅ‘่กŒๅˆ›้€ ๆ€ป่ฎก 336 ไบฟไธช CKB ไปฃๅธ๏ผŒไปฅๅฅ–ๅŠฑ็กฎไฟ็ฝ‘็ปœๅฎ‰ๅ…จ็š„็Ÿฟๅทฅใ€‚", + "halving_event_section_2": "ๆฏไธช Epoch๏ผˆ็บฆ 4 ไธชๅฐๆ—ถ๏ผ‰ๆŽจๅ‡บ 1,917,808 CKB ็š„ๅ›บๅฎš้‡‘้ขใ€‚ๆฏ 8,760 ไธช Epoch๏ผŒๅณๅคง็บฆๅ››ๅนด็š„ๆ—ถ้—ด๏ผŒ่ฟ™ไธ€ๆ•ฐ้ขไผšๅ‡ๅŠใ€‚่ฟ™ไธ€ไบ‹ไปถ่ขซ็งฐไธบๅ‡ๅŠ๏ผŒไนŸๅฐฑๆ˜ฏๅˆ็บงๅ‘่กŒ็š„ๆŒ–็Ÿฟๅฅ–ๅŠฑๆฐธไน…ๅ‡ๅฐ‘ 50%ใ€‚่ฟ™็งๅ‡ๅŠ่ฟ‡็จ‹ๅฐ†ๆฏๅ››ๅนดๆŒ็ปญไธ€ๆฌก๏ผŒ็›ดๅˆฐ 2103 ๅนดใ€‚", + "significance": "ๅ‡ๅŠ็š„ๆ„ไน‰ๆ˜ฏไป€ไนˆ๏ผŸ", + "significance_section_1": "ๆฏๆฌกๅ‡ๅŠ้ƒฝไผšๅฏผ่‡ดๆฏไธชๅŒบๅ—ไบง็”Ÿ็š„ๅฅ–ๅŠฑๆ€ฅๅ‰งไธ‹้™ใ€‚่ฟ›ๅ…ฅๆต้€š็š„ๆ–ฐ CKB ไพ›ๅบ”้‡ๅ‡ๅฐ‘๏ผŒ้€š่ดง่†จ่ƒ€็Ž‡ๅคงๅน…ไธ‹้™ใ€‚่ฟ™ไธ€็‚น้žๅธธ้‡่ฆ๏ผŒๅ› ไธบๅฎƒๆ”นๅ˜ไบ†ๆฝœๅœจ็š„ๅธ‚ๅœบๅนณ่กก๏ผŒ่ฟซไฝฟไบบไปฌ้‡ๆ–ฐ่ฏ„ไผฐไป€ไนˆๆ˜ฏๅ…ฌๅนณ็š„ๅธ‚ๅœบไปทๅ€ผใ€‚", + "significance_section_2": "ๅ‡ๅŠๆดปๅŠจๆŒ‰็…ง้ข„ๅฎš็š„ๅ‘่กŒๆ—ถ้—ด่กจ่ฟ›่กŒ๏ผŒไธ่ƒฝๆ›ดๆ”นใ€ๆŽจ่ฟŸๆˆ–ๅปถ่ฟŸใ€‚ๆŠ•่ต„่€…ๅ’Œ็คพๅŒบๆˆๅ‘˜้€šๅธธๆœŸๅพ…็€ๅ‡ๅŠไบ‹ไปถ็š„ๅ‘็”Ÿ๏ผŒๅ› ไธบ่ฟ™ๆ ‡ๅฟ—็€้กน็›ฎๅŽ†ๅฒไธŠ็š„ไธ€ไธช้‡่ฆ้‡Œ็จ‹็ข‘๏ผŒๅ€ผๅพ—ๅบ†็ฅใ€‚", + "how_does_work": "CKB ๅ‡ๅŠๆ˜ฏๅฆ‚ไฝ•่ฟ่กŒ็š„?", + "how_does_work_section_1": "ไธบไบ†ไฝฟๅ‡ๅŠ่ฎกๅˆ’ๆŒ‰้ข„ๆœŸ่ฟ่กŒ๏ผŒๆˆ‘ไปฌๅผ•ๅ…ฅไบ†ไธ€ไธชๅไธบ \"Epoch\"็š„ๆ—ถ้—ดๆฆ‚ๅฟตใ€‚", + "how_does_work_section_2": "ไธ€ไธช Epoch ๆ˜ฏไธ€็ป„ๅŒบๅ—็š„ไธ€ๆฎตๆ—ถ้—ดใ€‚", + "how_does_work_section_3": "ๅœจ Nervos ไธญ๏ผŒPoW ้šพๅบฆๅœจๆ–ฐ็š„ Epoch ไธญๅ‘็”Ÿๅ˜ๅŒ–ใ€‚ๅŒไธ€ Epoch ไธญ็š„ๆ‰€ๆœ‰ๅŒบๅ—ๅ…ฑไบซ็›ธๅŒ็š„้šพๅบฆ็›ฎๆ ‡ใ€‚้šพๅบฆ่ฐƒๆ•ด็ฎ—ๆณ•ๆ—จๅœจๅฐ†ๅญคๅ—็Ž‡็จณๅฎšๅœจ 2.5%๏ผŒๅฐ† Epoch ็จณๅฎšๅœจ 4 ๅฐๆ—ถใ€‚", + "how_does_work_section_4": "ๆฏๆฌกๅ‡ๅŠ็š„ Epoch ไธบ", + "how_does_work_section_5": "CKBytes ็š„็ฌฌ N ๆฌกๅ‡ๅŠ้ฆ–ๅ…ˆๅ‘็”Ÿๅœจ Epoch ไธŠ", + "how_does_work_section_6": "ๅ› ๆญค๏ผŒCKByte ๅ‡ๅŠไบ‹ไปถๅ‘็”ŸๅœจๆŒ‡ๅฎš็š„ Epoch๏ผŒไพ‹ๅฆ‚ 8760ใ€17520ใ€‚", + "when": "CKB ไฝ•ๆ—ถๅ‡ๅŠ?", + "when_section_1": "ไธ‹่กจ่ฏฆ็ป†ๅˆ—ๅ‡บไบ†ๅณๅฐ†่ฟ›่กŒ็š„ 10 ๆฌก CKB ๅŠไปทๅŠๅ…ถ็›ธๅบ”็š„ๅŸบๆœฌๅ‘่กŒๅฅ–ๅŠฑ็š„ๆ—ถ้—ด่กจ๏ผš", + "when_section_2": "ๆณจๆ„๏ผŒCKB ๅ—ๅฅ–ๅŠฑๅŒ…ๆ‹ฌ", + "when_section_3": "ๅŸบ็ก€๏ผˆๅ‘่กŒ๏ผ‰ๅฅ–ๅŠฑใ€ไบŒ็บง๏ผˆๅ‘่กŒ๏ผ‰ๅฅ–ๅŠฑใ€ๆไบคๅฅ–ๅŠฑ", + "when_section_4": "ๆๆกˆๅฅ–ๅŠฑ", + "when_section_5": "ไฝ†ๆ˜ฏ๏ผŒๅฝ“ๆˆ‘ไปฌ่ฎจ่ฎบ CKB ๅ‡ๅŠๆ—ถ๏ผŒๅฎƒๅชไธŽ", + "when_section_6": "ๆœ‰ๅ…ณใ€‚ๅ› ๆญค๏ผŒ่กจไธญๅˆ—ๅ‡บ็š„ๅŒบๅ—ๅฅ–ๅŠฑๅชๅŒ…ๆ‹ฌๅŸบ็ก€ๅ‘่กŒๅฅ–ๅŠฑ็š„้ƒจๅˆ†๏ผŒไปฅๅธฎๅŠฉ็†่งฃใ€‚", + "table_event": "ไบ‹ไปถ", + "table_date": "ๆ—ฅๆœŸ", + "table_epoch_number": "Epoch ๅบๅท", + "table_epoch_reward": "ๆฏไธช Epoch ๆŒ–็Ÿฟไบงๅ‡บ", + "table_block_reward": "ๅŒบๅ—ๅฅ–ๅŠฑ(ๆŒ‰ๆฏ epoch ๆœ‰1800ไธชๅŒบๅ—่ฎก็ฎ—)", + "table_daily_reward": "ๆฏๆ—ฅๆŒ–็Ÿฟไบงๅ‡บ", + "table_total": "ๆœŸ้—ด็ดฏ่ฎกๆŒ–็Ÿฟไบงๅ‡บ", + "table_launches": "Nervos ไธป็ฝ‘ไธŠ็บฟ", + "genesis_epoch": "ๅˆ›ไธ–็บชๅ…ƒ", + "expected": "้ข„่ฎก", + "share_text": "๐Ÿ“ข @NervosNetwork CKB ้ข„่ฎกไบŽ {{date}} ่ฟŽๆฅ{{times}}ๅ‡ๅŠ๐Ÿš€๏ผŒ\n\n๐Ÿ“…่ท็ฆปๅ‡ๅŠๅ€’่ฎกๆ—ถ่ฟ˜ๆœ‰ {{countdown}}ใ€‚\n\n๐Ÿ”— ่ฎฟ้—ฎ๐Ÿ‘‰ https://explorer.nervos.org ไบ†่งฃๆ›ดๅคš" + }, "glossary": { "block_height": "ๅŒบๅ—้ซ˜ๅบฆ๏ผŒๅˆ็งฐไธบๅŒบๅ—็ผ–ๅท๏ผŒ่กจ็คบๅŒบๅ—้“พ็š„้•ฟๅบฆ๏ผŒๅœจๆทปๅŠ ๆ–ฐๅŒบๅ—ๅŽไผšๅขžๅŠ ใ€‚", "cell": "CKB ้‡‡็”จไบ†ๆ›ด้€š็”จ็š„ Cell ๆจกๅž‹๏ผŒ่€Œไธๆ˜ฏ UTXO ๆจกๅž‹ใ€‚ไบคๆ˜“ไผš้”€ๆฏๅ…ˆๅ‰ไบคๆ˜“ไธญๅˆ›ๅปบ็š„ๆŸไบ›่พ“ๅ‡บ๏ผŒๅนถๅˆ›ๅปบไธ€ไบ›ๆ–ฐ็š„่พ“ๅ‡บใ€‚ๆฏไธชไบคๆ˜“็š„่พ“ๅ‡บ้ƒฝๆ˜ฏไธ€ไธช Cellใ€‚", diff --git a/src/pages/Address/AddressComp.tsx b/src/pages/Address/AddressComp.tsx index b70e2e717..4cdf29964 100644 --- a/src/pages/Address/AddressComp.tsx +++ b/src/pages/Address/AddressComp.tsx @@ -4,10 +4,10 @@ import { useQuery } from 'react-query' import { Radio } from 'antd' import { Base64 } from 'js-base64' import { hexToBytes } from '@nervosnetwork/ckb-sdk-utils' +import { TFunction, useTranslation } from 'react-i18next' import OverviewCard, { OverviewItemData } from '../../components/Card/OverviewCard' import TransactionItem from '../../components/TransactionItem/index' import { explorerService } from '../../services/ExplorerService' -import i18n from '../../utils/i18n' import { parseSporeCellData } from '../../utils/spore' import { localeNumberString, parseUDTAmount } from '../../utils/number' import { shannonToCkb, deprecatedAddrToNewAddr, handleNftImgError, patchMibaoImg } from '../../utils/util' @@ -48,26 +48,26 @@ import { omit } from '../../utils/object' import { CsvExport } from '../../components/CsvExport' import PaginationWithRear from '../../components/PaginationWithRear' -const addressAssetInfo = (address: State.Address, useMiniStyle: boolean) => { +const addressAssetInfo = (address: State.Address, useMiniStyle: boolean, t: TFunction) => { const items = [ { title: '', content: '', }, { - title: i18n.t('address.occupied'), - tooltip: i18n.t('glossary.occupied'), + title: t('address.occupied'), + tooltip: t('glossary.occupied'), content: , isAsset: true, }, { icon: CKBTokenIcon, - title: i18n.t('common.ckb_unit'), + title: t('common.ckb_unit'), content: , }, { - title: i18n.t('address.dao_deposit'), - tooltip: i18n.t('glossary.nervos_dao_deposit'), + title: t('address.dao_deposit'), + tooltip: t('glossary.nervos_dao_deposit'), content: , isAsset: true, }, @@ -76,9 +76,9 @@ const addressAssetInfo = (address: State.Address, useMiniStyle: boolean) => { content: '', }, { - title: i18n.t('address.compensation'), + title: t('address.compensation'), content: , - tooltip: i18n.t('glossary.nervos_dao_compensation'), + tooltip: t('glossary.nervos_dao_compensation'), isAsset: true, }, ] as OverviewItemData[] @@ -100,6 +100,7 @@ const UDT_LABEL: Record = { } const AddressUDTItem = ({ udtAccount }: { udtAccount: State.UDTAccount }) => { + const { t } = useTranslation() const { symbol, uan, amount, udtIconFile, typeHash, udtType, collection, cota } = udtAccount const isSudt = udtType === 'sudt' const isSpore = udtType === 'spore_cell' @@ -168,7 +169,7 @@ const AddressUDTItem = ({ udtAccount }: { udtAccount: State.UDTAccount }) => { return (
    - {isUnverified ? `${i18n.t('udt.unverified')}: ` : null} + {isUnverified ? `${t('udt.unverified')}: ` : null} {UDT_LABEL[udtType] ?? 'unknown'}
    @@ -211,16 +212,17 @@ const lockScriptIcon = (show: boolean) => { return isMainnet() ? ArrowDownIcon : ArrowDownBlueIcon } -const getAddressInfo = ({ liveCellsCount, minedBlocksCount, type, addressHash, lockInfo }: State.Address) => { +const useAddressInfo = ({ liveCellsCount, minedBlocksCount, type, addressHash, lockInfo }: State.Address) => { + const { t } = useTranslation() const items: OverviewItemData[] = [ { - title: i18n.t('address.live_cells'), - tooltip: i18n.t('glossary.live_cells'), + title: t('address.live_cells'), + tooltip: t('glossary.live_cells'), content: localeNumberString(liveCellsCount), }, { - title: i18n.t('address.block_mined'), - tooltip: i18n.t('glossary.block_mined'), + title: t('address.block_mined'), + tooltip: t('glossary.block_mined'), content: localeNumberString(minedBlocksCount), }, ] @@ -228,22 +230,22 @@ const getAddressInfo = ({ liveCellsCount, minedBlocksCount, type, addressHash, l if (type === 'LockHash') { if (!addressHash) { items.push({ - title: i18n.t('address.address'), - content: i18n.t('address.unable_decode_address'), + title: t('address.address'), + content: t('address.unable_decode_address'), }) } else { items.push({ - title: i18n.t('address.address'), + title: t('address.address'), contentWrapperClass: styles.addressWidthModify, content: {addressHash}, }) } } if (lockInfo && lockInfo.epochNumber !== '0' && lockInfo.estimatedUnlockTime !== '0') { - const estimate = Number(lockInfo.estimatedUnlockTime) > new Date().getTime() ? i18n.t('address.estimated') : '' + const estimate = Number(lockInfo.estimatedUnlockTime) > new Date().getTime() ? t('address.estimated') : '' items.push({ - title: i18n.t('address.lock_until'), - content: `${lockInfo.epochNumber} ${i18n.t('address.epoch')} (${estimate} ${parseSimpleDateNoSecond( + title: t('address.lock_until'), + content: `${lockInfo.epochNumber} ${t('address.epoch')} (${estimate} ${parseSimpleDateNoSecond( lockInfo.estimatedUnlockTime, )})`, }) @@ -253,12 +255,13 @@ const getAddressInfo = ({ liveCellsCount, minedBlocksCount, type, addressHash, l const AddressLockScript: FC<{ address: State.Address }> = ({ address }) => { const [showLock, setShowLock] = useState(false) + const { t } = useTranslation() return ( - + setShowLock(!showLock)}> -
    {i18n.t('address.lock_script')}
    +
    {t('address.lock_script')}
    lock script
    {showLock && address.lockScript &&
  • {i18n.t('nft.tx_hash')}{i18n.t('nft.action')}{t('nft.tx_hash')}{t('nft.action')} setIsShowInAge(show => !show)} className={styles.age} - title={i18n.t('nft.toggle-age')} + title={t('nft.toggle-age')} style={{ color: primaryColor, }} > - {i18n.t('nft.age')} + {t('nft.age')} {i18n.t('nft.from')}{i18n.t('nft.to')}{t('nft.from')}{t('nft.to')}
    {i18n.t(`nft.action_type.${item.action}`)}{t(`nft.action_type.${item.action}`)} {isShowInAge ? dayjs(item.transaction.block_timestamp).fromNow() @@ -133,7 +138,7 @@ const NftItemTransfers: React.FC<{ list: TransferListRes['data']; isLoading: boo ) : (
    - {isLoading ? i18n.t('nft.loading') : i18n.t(`nft.no_record`)} + {isLoading ? t('nft.loading') : t(`nft.no_record`)}