完善多語言

This commit is contained in:
john 2022-05-10 20:33:42 +07:00
parent e2ceff4a4d
commit 193ed837b4
19 changed files with 258 additions and 80 deletions

View File

@ -1 +1,2 @@
DOMAIN='good'
REACT_APP_NAME = CoinWind
REACT_APP_I18N_ENABLED = true

View File

@ -1,8 +1,8 @@
import React from 'react';
import {
ChakraProvider,
theme,
ChakraProvider, ColorModeScript,
} from '@chakra-ui/react';
import { theme } from './theme'
import { Layout } from './components';
import { Outlet } from 'react-router-dom'
import { AppContextProvider } from './AppContext';
@ -12,6 +12,7 @@ function App() {
return (
<ChakraProvider theme={theme}>
<AppContextProvider>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<Layout>
<Outlet />
</Layout>

View File

@ -1,7 +1,7 @@
import React from "react"
import { config } from "./config"
import { get_settings } from './api'
import { ImTwitter, ImTelegram, ImFacebook } from "react-icons/im";
import { ImTwitter, ImTelegram, ImFacebook, ImWhatsapp } from "react-icons/im";
const AppContext = React.createContext(null)
@ -10,9 +10,10 @@ export const useApp = () => {
}
let SOCIALS = [
{ name: 'telegram', icon: ImTelegram, path: 'https://telegram.org' },
{ name: 'twitter', icon: ImTwitter, path: 'https://twitter.com' },
{ name: 'facebook', icon: ImFacebook, path: 'https://facebook.com' },
{ name: 'telegram', icon: ImTelegram, path: 'https://telegram.org', enabled: false },
{ name: 'twitter', icon: ImTwitter, path: 'https://twitter.com', enabled: false },
{ name: 'facebook', icon: ImFacebook, path: 'https://facebook.com', enabled: false },
{ name: 'whatsapp', icon: ImWhatsapp, path: 'https://whatsapp.com', enabled: true },
]
export const AppContextProvider = ({ children }) => {

View File

@ -2,7 +2,8 @@ import React from 'react'
import {
Button, Flex, VStack, HStack, IconButton,
useColorModeValue, useDisclosure, Icon, Text,
useToast,
useToast, Stack,
Select,
} from '@chakra-ui/react'
import { FiMenu, FiCircle } from 'react-icons/fi'
import { Logo } from './Logo'
@ -10,9 +11,12 @@ import { ColorModeSwitcher } from './ColorModeSwitcher'
import { config } from '../config'
import { useApp } from '../AppContext'
import { AlertBox } from './alert'
import { Lang } from './Lang'
import { connectWallet, addEventListeners, fetchAccount } from '../lib'
import { get_register } from '../api'
import { useSearchParams } from 'react-router-dom'
import { supportedLangs } from '../i18'
import { useTranslation } from 'react-i18next'
export const AppBar = ({ onOpenDrawer, ...rest }) => {
@ -20,6 +24,7 @@ export const AppBar = ({ onOpenDrawer, ...rest }) => {
const toast = useToast()
const [searchParams, setSearchParams] = useSearchParams()
const { isOpen: isOpenConnectedWallet, onOpen: onOpenConnectedWallet, onClose: onCloseConnectedWallet } = useDisclosure()
const { t } = useTranslation()
const bg = useColorModeValue('white', 'gray.900')
const borderBottomColor = useColorModeValue('gray.200', 'gray.700')
@ -61,6 +66,10 @@ export const AppBar = ({ onOpenDrawer, ...rest }) => {
}
}
const onSelectedLang = (ev) => {
console.log(ev.target.value)
}
// auto connect
React.useEffect(() => {
if (config.AUTO_CONN_WALLET) {
@ -94,7 +103,7 @@ export const AppBar = ({ onOpenDrawer, ...rest }) => {
onClick={onBtnConnect}
>
{
app.address ? 'Connected' : 'Connect Wallet'
app.address ? t('connected') : t('connectWallet')
}
</Button>
@ -109,6 +118,12 @@ export const AppBar = ({ onOpenDrawer, ...rest }) => {
<ColorModeSwitcher
display={{ base: 'none', md: 'flex' }}
/>
<Stack
display={{ base: 'none', md: 'flex' }}
>
<Lang config={supportedLangs} />
</Stack>
</HStack>
<AlertBox

View File

@ -1,6 +1,7 @@
import { Box, Center, HStack, VStack, Text, Image, useColorModeValue } from "@chakra-ui/react"
import { Images } from '../data'
import React from "react"
import { useTranslation } from "react-i18next"
/**
* list auditors
@ -10,6 +11,7 @@ import React from "react"
export const Auditors = ({ ...rest }) => {
const bg = useColorModeValue('white', 'gray.900')
const { t } = useTranslation()
return (
<VStack
@ -26,7 +28,7 @@ export const Auditors = ({ ...rest }) => {
borderBottom="1px"
borderColor='gray.200'
>
Auditors
{t('auditors')}
</Text>
</Center>
<HStack

View File

@ -6,6 +6,7 @@ import {
useColorModeValue,
} from "@chakra-ui/react"
import { Images } from '../data'
import { useTranslation } from 'react-i18next'
const Row = ({ children }) => {
return (
@ -50,6 +51,7 @@ const CardRow = ({ title, value, compond = false }) => {
export const FarmCoinCard = ({ index, icon, symbol, apy, deposited, vl,
remaining, loading = false, isNew = false, authorized = false, onWithdrawal = null, onMining = null }) => {
const { t } = useTranslation()
const bg = useColorModeValue('white', 'gray.900')
return (
@ -84,7 +86,7 @@ export const FarmCoinCard = ({ index, icon, symbol, apy, deposited, vl,
<Text
color="gray.400"
fontSize="10"
>Harvest {symbol}</Text>
>{t('harvest') + ' ' + symbol}</Text>
{
isNew && <Image
@ -97,13 +99,14 @@ export const FarmCoinCard = ({ index, icon, symbol, apy, deposited, vl,
</HStack>
<CardRow title="APY" value={apy} compond="true" />
<CardRow title="Deposited" value={deposited} />
<CardRow title="VL" value={vl} />
<CardRow title="Remaining" value={remaining} />
<CardRow title={t('apy')} value={apy} compond="true" />
<CardRow title={t('deposited')} value={deposited} />
<CardRow title={t('vl')} value={vl} />
<CardRow title={t('remaining')} value={remaining} />
<HStack
w="full"
my="4"
>
<Slider aria-label='slider-ex-4' defaultValue={deposited} min={0} max={vl}>
<SliderTrack bg='red.100'>
@ -120,13 +123,13 @@ export const FarmCoinCard = ({ index, icon, symbol, apy, deposited, vl,
<Button w="30" h="10"
onClick={() => { onWithdrawal && onWithdrawal(index) }}
>
Withdrawal
{t('withdrawal')}
</Button>
<Button w="30" h="10" colorScheme="teal" isLoading={loading}
onClick={() => { onMining && onMining(index) }}
>
{
authorized ? 'Deposite' : 'Mining'
authorized ? t('deposite') : t('mining')
}
</Button>
</HStack>

33
src/components/Lang.js Normal file
View File

@ -0,0 +1,33 @@
import React from "react"
import { Select } from '@chakra-ui/react'
import { useTranslation } from "react-i18next"
import { useLocalStorage } from "../hooks"
/**
*
* @param {array} config: language config. eg [{lang:'en', name:'English'}, {lang:'fr', name:'French'} ]
*/
export const Lang = ({ config }) => {
const [sel, setSel] = useLocalStorage('lang', 'en')
const { i18n } = useTranslation()
const onSelectedLang = (ev) => {
i18n.changeLanguage(ev.target.value)
setSel(ev.target.value)
}
React.useEffect(() => {
i18n.changeLanguage(sel)
}, [])
return (
<Select value={sel} onChange={onSelectedLang}>
{
config.map((item) => (
<option key={item.lang} value={item.lang}>{item.name}</option>
))
}
</Select>
)
}

View File

@ -6,10 +6,13 @@ import {
import { ImArrowRight2 } from 'react-icons/im'
import { config } from '../config'
import { Link, useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
const FarmListItem = ({ id, img, symbol, percentage, ...rest }) => {
const navigateTo = useNavigate()
const { t } = useTranslation()
const onBtnMining = () => {
navigateTo('/farm')
}
@ -33,7 +36,9 @@ const FarmListItem = ({ id, img, symbol, percentage, ...rest }) => {
</Text>
<Button colorScheme="teal" size='sm'
onClick={onBtnMining}
>Mining</Button>
>
{t('mining')}
</Button>
</HStack>
)
}
@ -41,6 +46,7 @@ const FarmListItem = ({ id, img, symbol, percentage, ...rest }) => {
export const MiningListCard = ({ coins, ...rest }) => {
const bg = useColorModeValue('white', 'gray.900')
const { t } = useTranslation()
return (
<Box>
@ -63,7 +69,7 @@ export const MiningListCard = ({ coins, ...rest }) => {
alignItems="center"
>
<Box>
<Text>Defi Farm</Text>
<Text>{t('defiFarm')}</Text>
</Box>
<Link to="/farm">
@ -75,7 +81,8 @@ export const MiningListCard = ({ coins, ...rest }) => {
alignItems="center"
>
<Text>More</Text><Icon as={ImArrowRight2} />
<Text>{t('more')}</Text>
<Icon as={ImArrowRight2} />
</Flex>
</Link>

View File

@ -1,11 +1,16 @@
import { Box } from "@chakra-ui/react"
import { Box, Stack } from "@chakra-ui/react"
export const Panel = ({...props}) => {
export const Panel = ({ children, ...rest }) => {
return (
<Box>
</Box>
<Stack
w="full"
borderRadius="lg"
direction={{ base: 'column', md: 'row' }}
{...rest}
>
{children}
</Stack>
)
}

View File

@ -1,22 +1,16 @@
import {
Box, CloseButton, Flex, Icon, useColorModeValue,
Text, Box, CloseButton, Flex, Icon, useColorModeValue,
Stack, Select,
} from '@chakra-ui/react'
import { FiArrowRight, FiHome, FiTrendingUp } from 'react-icons/fi'
import { useApp } from '../AppContext'
import { Logo } from './Logo'
import { ColorModeSwitcher } from './ColorModeSwitcher'
import { Link } from 'react-router-dom';
const menuItems = [
{ name: 'Home', icon: FiHome, path: '/home' },
{ name: 'Farm', icon: FiTrendingUp, path: '/farm' },
]
const docItems = [
{ name: 'Announcement', icon: '', path: '' },
{ name: 'FAQ', icon: '', path: '' },
{ name: 'Tutorial', icon: '', path: '' },
]
import { useTranslation } from 'react-i18next'
import { useLocalStorage } from '../hooks'
import { Lang } from './Lang'
import { supportedLangs } from '../i18'
const NavItem = ({ icon, path, onClick, children, ...rest }) => {
return (
@ -78,8 +72,20 @@ export const SideBar = ({ onClose, ...rest }) => {
const app = useApp()
const bg = useColorModeValue('white', 'gray.900')
const { t, i18n } = useTranslation()
// const colorBorderRight = useColorModeValue('gray.200', 'gray.700')
const menuItems = [
{ name: t('home'), icon: FiHome, path: '/home', enabled: true },
{ name: t('farm'), icon: FiTrendingUp, path: '/farm', enabled: true },
]
const docItems = [
{ name: t('announcement'), icon: '', path: '', enabled: true },
{ name: t('faq'), icon: '', path: '', enabled: true },
{ name: t('tutorial'), icon: '', path: '', enabled: true },
]
return (
<Box
transition="3s ease"
@ -94,7 +100,8 @@ export const SideBar = ({ onClose, ...rest }) => {
<Flex h="20"
alignItems="center"
mx="8"
justifyContent="space-between">
justifyContent="space-between"
>
<Logo />
<ColorModeSwitcher
@ -106,10 +113,19 @@ export const SideBar = ({ onClose, ...rest }) => {
<Flex h="90%" direction="column" justifyContent="space-between">
<Box>
<Stack
display={{ base: 'flex', md: 'none' }}
w="30"
mx="8"
my="4"
>
<Lang config={supportedLangs} />
</Stack>
{/* nav links */}
{
menuItems.map((item) => (
<NavItem key={item.name} icon={item.icon} onClick={onClose} path={item.path}>
item.enabled && <NavItem key={item.name} icon={item.icon} onClick={onClose} path={item.path}>
{item.name}
</NavItem>
))
@ -120,7 +136,7 @@ export const SideBar = ({ onClose, ...rest }) => {
{/* document links */}
{
docItems.map((item) => (
<DocItem key={item.name} icon={item.icon} path={item.path}>
item.enabled && <DocItem key={item.name} icon={item.icon} path={item.path}>
{item.name}
</DocItem>
))
@ -136,7 +152,7 @@ export const SideBar = ({ onClose, ...rest }) => {
>
{
app.socials.map((item) => (
<SocialItem key={item.name} icon={item.icon} path={item.path} />
item.enabled && <SocialItem key={item.name} icon={item.icon} path={item.path} />
))
}
</Flex>

1
src/hooks/index.js Normal file
View File

@ -0,0 +1 @@
export * from './useLocalStorage'

View File

@ -27,7 +27,7 @@ function getStorageJson(name, default_value) {
* const [state, setState] = useLocalStorage('mystate', 0)
* }
*/
export default useLocalStorage = (name, default_value) => {
export const useLocalStorage = (name, default_value) => {
const [value, setValue] = React.useState(() => getStorageJson(name, default_value))
React.useEffect(() => {

View File

@ -1,48 +1,20 @@
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import { en, fr, tw } from './lang'
const resources = {
en: {
translation: {
home: 'Home',
farm: 'farm',
announcement: 'Announcement',
faq: 'FAQ',
tutorial: 'tutorial',
connectWallet: 'Connect Wallet',
tvl: 'TVL',
totalUsersEarned: 'Total Users Earned',
personalTvl: 'Personal TVL',
totalPersonalEarned: 'Total Personal Earned',
harvest: 'Harvest',
apy: 'APY',
compoundInterest: 'Compound Interest',
deposite: 'Deposite',
vl: 'VL',
remaining: 'Remaining',
withdrawal: 'Withdrawal',
mining: 'Mining',
defiFarm: 'Defi Farm',
multiChainLockupValue: 'Multi-chain Lock-up Value',
multiChainUserRevenue: 'Multi-chain User Revenue',
miningOutput: 'Mining Output',
miningOutputValue: 'Mining Output Value',
multiChainReward: 'Multi-chain Reward',
multiChainBurned: 'Multi-chain Burned',
more: 'More',
partners: 'Partners',
auditors: 'Auditors',
}
translation: en,
name: 'English',
},
fr: {
translation: {
}
translation: fr,
name: 'Français',
},
tw: {
translation: {
}
translation: tw,
name: '中文繁體',
},
}
@ -56,4 +28,17 @@ i18n.use(initReactI18next).init({
}
})
const parseResources = () => {
let supported = []
for (const key in resources) {
supported.push({
'lang': `${key}`,
'name': `${resources[key]['name']}`,
})
}
return supported
}
export const supportedLangs = parseResources()
export default i18n;

View File

@ -14,7 +14,6 @@ const root = createRoot(element)
root.render(
<StrictMode>
<BrowserRouter>
<ColorModeScript />
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
@ -23,7 +22,6 @@ root.render(
<Route path="*" element={<div>Not Found</div>} />
</Route>
</Routes>
</BrowserRouter>
</StrictMode >
)

View File

@ -0,0 +1,33 @@
export const en = {
home: 'Home',
farm: 'Farm',
announcement: 'Announcement',
faq: 'FAQ',
tutorial: 'tutorial',
connectWallet: 'Connect Wallet',
connected: 'Connected',
tvl: 'TVL',
totalUsersEarned: 'Total Users Earned',
personalTvl: 'Personal TVL',
totalPersonalEarned: 'Total Personal Earned',
harvest: 'Harvest',
apy: 'APY',
compoundInterest: 'Compound Interest',
deposite: 'Deposite',
deposited: 'Deposited',
vl: 'VL',
remaining: 'Remaining',
withdrawal: 'Withdrawal',
mining: 'Mining',
defiFarm: 'DeFi Farm',
multiChainLockupValue: 'Multi-chain Lock-up Value',
multiChainUserRevenue: 'Multi-chain User Revenue',
miningOutput: 'Mining Output',
miningOutputValue: 'Mining Output Value',
multiChainReward: 'Multi-chain Reward',
multiChainBurned: 'Multi-chain Burned',
more: 'More',
partners: 'Partners',
auditors: 'Auditors',
}

View File

@ -0,0 +1,33 @@
export const fr = {
home: 'Domicile',
farm: 'Ferme',
announcement: 'Annonce',
faq: 'FAQ',
tutorial: 'Didacticiel',
connectWallet: 'Connecter le portefeuille',
connected: 'Connecté',
tvl: 'TVL',
totalUsersEarned: 'Total Users Earned',
personalTvl: 'Personal TVL',
totalPersonalEarned: 'Total Personal Earned',
harvest: 'Harvest',
apy: 'APY',
compoundInterest: 'Compound Interest',
deposite: 'Deposite',
deposited: 'Deposited',
vl: 'VL',
remaining: 'Remaining',
withdrawal: 'Withdrawal',
mining: 'Mining',
defiFarm: 'DeFi Farm',
multiChainLockupValue: 'Multi-chain Lock-up Value',
multiChainUserRevenue: 'Multi-chain User Revenue',
miningOutput: 'Mining Output',
miningOutputValue: 'Mining Output Value',
multiChainReward: 'Multi-chain Reward',
multiChainBurned: 'Multi-chain Burned',
more: 'More',
partners: 'Partners',
auditors: 'Auditors',
}

3
src/lang/index.js Normal file
View File

@ -0,0 +1,3 @@
export * from './en'
export * from './fr'
export * from './tw'

View File

@ -0,0 +1,33 @@
export const tw = {
home: '首頁',
farm: '農場',
announcement: '公告',
faq: 'FAQ',
tutorial: '教案',
connectWallet: '連接錢包',
connected: '已連接',
tvl: '總鎖倉價值',
totalUsersEarned: 'Total Users Earned',
personalTvl: 'Personal TVL',
totalPersonalEarned: 'Total Personal Earned',
harvest: 'Harvest',
apy: '年度百分收益率',
compoundInterest: 'Compound Interest',
deposite: '存入',
deposited: '已存入',
vl: 'VL',
remaining: 'Remaining',
withdrawal: '取出',
mining: '挖礦',
defiFarm: 'DeFi 農場',
multiChainLockupValue: 'Multi-chain Lock-up Value',
multiChainUserRevenue: 'Multi-chain User Revenue',
miningOutput: 'Mining Output',
miningOutputValue: 'Mining Output Value',
multiChainReward: 'Multi-chain Reward',
multiChainBurned: 'Multi-chain Burned',
more: '更多',
partners: 'Partners',
auditors: 'Auditors',
}

8
src/theme.js Normal file
View File

@ -0,0 +1,8 @@
import { extendTheme } from "@chakra-ui/react"
const config = {
initialColorMode: 'light',
useSystemColorMode: false,
}
export const theme = extendTheme({config})