Add i18n header components (#1466)

pull/1457/head
bobowiki 1 year ago committed by GitHub
parent f1957dda88
commit 4abdf0a15b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -33,6 +33,11 @@ module.exports = {
},
},
],
// style: {
// modules: {
// localIdentName: '[local]_[hash:base64:5]', // 可以自定义你的类名生成规则
// },
// },
webpack: {
alias: {
'@': resolve('src'),

@ -3,12 +3,15 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^5.2.6",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"ahooks": "^3.7.8",
"antd": "^5.4.7",
"dayjs": "^1.11.9",
"i18next": "^23.5.1",
"i18next-browser-languagedetector": "^7.1.0",
"qs": "^6.11.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Dashboard</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

@ -0,0 +1,14 @@
.header-wrapper {
display: flex;
width: 100%;
justify-content: space-between;
.logo {
display: flex;
flex-direction: column;
width: 200px;
img {
flex: 1;
object-fit: contain;
}
}
}

@ -0,0 +1,86 @@
import React, { useContext } from 'react';
import style from './index.module.less';
import { UserOutlined } from '@ant-design/icons';
import { Avatar, Button, Col, Dropdown, Row, Switch, Tag } from 'antd';
import useThemeMode from '@/hooks/useThemeMode';
import { MyContext } from '@/context';
import IconFont from '@/components/icon';
const HeaderChild = () => {
const { isDark, setIsDark } = useThemeMode();
const { lang, setLang } = useContext<any>(MyContext);
const items = [
{
key: '1',
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.antgroup.com">
1st menu
</a>
),
},
{
key: '2',
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.aliyun.com">
2nd menu
</a>
),
},
{
key: '3',
label: (
<a target="_blank" rel="noopener noreferrer" href="https://www.luohanacademy.com">
</a>
),
},
];
return (
<div className={style['header-wrapper']}>
<div className={style['logo']}>
<img src="https://demo.knowstreaming.com/layout/assets/image/ks-logo.png" alt="" />
</div>
<div className={style['edit-container']}>
<Row gutter={[16, 16]}>
<Col></Col>
<Col>
<Dropdown menu={{ items }} placement="bottomRight" trigger={['click']}>
<Avatar size={30} icon={<UserOutlined />} style={{ cursor: 'pointer' }} />
</Dropdown>
</Col>
<Col>
{/* <Tag
onClick={() => {
lang === 'zh' ? setLang('en') : setLang('zh');
}}
style={{ cursor: 'pointer' }}
>
{lang === 'zh' ? 'zh' : 'en'}
</Tag> */}
<Button
type="link"
onClick={() => {
lang === 'zh' ? setLang('en') : setLang('zh');
}}
>
<IconFont type="icon-qiehuanyuyan"></IconFont>
</Button>
</Col>
<Col>
<Switch
checkedChildren={'🌞'}
unCheckedChildren={'🌛'}
defaultChecked={isDark}
onChange={e => {
setIsDark(e);
}}
></Switch>
</Col>
</Row>
</div>
</div>
);
};
export default HeaderChild;

@ -0,0 +1,15 @@
import { createFromIconfontCN } from '@ant-design/icons';
interface Props {
type: string;
}
const MyIcon = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4254722_1xl4w1k5c53.js', // 在 iconfont.cn 上生成
});
const IconFont = (props: Props) => {
return <MyIcon {...props}></MyIcon>;
};
export default IconFont;

@ -4,6 +4,9 @@
.header {
height: 48px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.01), 0 3px 6px 3px rgba(0, 0, 0, 0.01), 0 2px 6px 0 rgba(0, 0, 0, 0.03);
padding: 0 20px;
display: flex;
align-items: center;
}
.sider {
height: calc(100vh - 48px);

@ -1,7 +1,7 @@
import { useState, useContext, ReactNode } from 'react';
import { DefaultTheme, ThemeContext } from 'styled-components';
import { Layout, Button, Menu } from 'antd';
import useThemeMode from '@/hooks/useThemeMode';
import { Layout, Menu } from 'antd';
import HeaderChild from '../header';
import { IMenuList } from '@/typings';
import style from './index.module.less';
@ -19,11 +19,11 @@ const LayoutCom = (props: ILayoutCom) => {
const onClick = (e: any) => {
setCurrent(e.key);
};
const [setIsDark] = useThemeMode();
return (
<main className={style.container} style={{ backgroundColor: myThemes.backgroundColor.bg1 }}>
<Header className={style.header}>
<Button onClick={() => setIsDark(pre => !pre)}></Button>
<HeaderChild />
</Header>
<Layout style={{ backgroundColor: myThemes.backgroundColor.bg1, height: 'calc(100vh - 64px)' }}>
{isSider && (

@ -2,10 +2,10 @@ import React, { useContext, useEffect, useState } from 'react';
import { DefaultTheme, ThemeProvider } from 'styled-components';
import { ConfigProvider, theme } from 'antd';
import { darkAlgorithm } from '../../theme/dark-algorithm';
import { defaultAlgorithm } from '../../theme/default-algnorithm';
import { lightDefaultTheme, darkDefaultTheme } from '../../theme';
import { MyThemeContext, THEME_NAME } from '../../context/themeContext';
import { darkAlgorithm } from '../../config/theme/dark-algorithm';
import { defaultAlgorithm } from '../../config/theme/default-algnorithm';
import { lightDefaultTheme, darkDefaultTheme } from '../../config/theme';
import { MyContext, THEME_NAME } from '../../context';
import zhCN from 'antd/es/locale/zh_CN';
interface ThemeProps {
@ -15,7 +15,7 @@ interface ThemeProps {
const ThemeComponent = ({ children }: ThemeProps) => {
const [themes, setThemes] = useState(defaultAlgorithm);
const [myThemes, setMyThemes] = useState<DefaultTheme>(lightDefaultTheme);
const { themeName } = useContext<any>(MyThemeContext);
const { themeName } = useContext<any>(MyContext);
const changeMode = (themeName: THEME_NAME) => {
if (themeName === THEME_NAME.DARK) {

@ -0,0 +1,28 @@
// i18n.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import zh from './locales/zh';
import en from './locales/en';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
zh: {
translation: zh,
},
en: {
translation: en,
},
},
debug: true,
fallbackLng: 'zh', // 默认语言
interpolation: {
escapeValue: false, // 不转义HTML标签
},
});
export default i18n;

@ -0,0 +1,5 @@
const enTranslationMap: { [key: string]: string } = {
hello: 'hello',
};
export default enTranslationMap;

@ -0,0 +1,5 @@
const zhTranslationMap: { [key: string]: string } = {
hello: '你好',
};
export default zhTranslationMap;

@ -2,7 +2,7 @@ import { theme } from 'antd';
import { darkDefaultTheme } from '.';
export const darkAlgorithm = {
token: {
borderRadius: 2,
borderRadius: 6,
colorPrimary: darkDefaultTheme.primary,
fontSize: 14,
},
@ -15,8 +15,8 @@ export const darkAlgorithm = {
fontSize: 14,
},
Table: {
borderRadius: 0,
borderRadiusLG: 0,
// borderRadius: 0,
// borderRadiusLG: 0,
padding: 10,
paddingXS: 5,
margin: 0,

@ -0,0 +1,45 @@
import { theme } from 'antd';
import { lightDefaultTheme } from '.';
export const defaultAlgorithm = {
token: {
borderRadius: 6,
colorPrimary: lightDefaultTheme.primary,
fontSize: 14,
// colorBgBase: lightDefaultTheme.backgroundColor.bg1,
},
components: {
Layout: {
bodyBg: lightDefaultTheme.backgroundColor.bg1,
headerBg: lightDefaultTheme.backgroundColor.bgHeader,
},
// Button: {
// fontSize: 14,
// },
// Table: {
// padding: 10,
// paddingXS: 5,
// margin: 0,
// fontSize: 14,
// colorBorderSecondary: lightDefaultTheme.borderColor.bl1,
// paddingContentVerticalLG: 4,
// },
// Modal: {
// borderRadiusLG: 2,
// borderRadiusSM: 2,
// colorText: lightDefaultTheme.fontColor.fc3,
// borderRadius: 2,
// paddingContentHorizontalLG: 0,
// paddingMD: 0,
// },
// Menu: {
// itemBg: lightDefaultTheme.backgroundColor.bg1,
// activeBarWidth: 0,
// activeBarHeight: 0,
// activeBarBorderWidth: 0,
// subMenuItemBorderRadius: 8,
// horizontalItemBorderRadius: 8,
// itemBorderRadius: 8,
// },
},
algorithm: theme.defaultAlgorithm,
};

@ -1,18 +1,67 @@
import React, { createContext, useState, ReactNode } from 'react';
import React, { createContext, useState, ReactNode, useEffect } from 'react';
import { ConfigProvider, theme } from 'antd';
import { DefaultTheme, ThemeProvider } from 'styled-components';
import enUS from 'antd/locale/en_US';
import zhCN from 'antd/locale/zh_CN';
import { darkDefaultTheme, lightDefaultTheme } from '@/config/theme';
import { defaultAlgorithm } from '@/config/theme/default-algnorithm';
import { darkAlgorithm } from '@/config/theme/dark-algorithm';
import { useTranslation } from 'react-i18next';
export enum THEME_NAME {
DEFAULT = 'default',
DARK = 'dark',
}
export const StoreContext = createContext<{
export enum LANG_NAME {
ZH = 'zh',
EN = 'en',
}
export const MyContext = createContext<{
themeName: string;
lang: LANG_NAME;
setThemeName: (name: THEME_NAME) => void;
setLang: (lang: LANG_NAME) => void;
} | null>(null);
export const Store: React.FC<{
export const MyStore: React.FC<{
children: ReactNode;
}> = ({ children }) => {
const [themeName, setThemeName] = useState<string>(THEME_NAME.DEFAULT);
return <StoreContext.Provider value={{ themeName, setThemeName }}>{children}</StoreContext.Provider>;
const [themeName, setThemeName] = useState<THEME_NAME>(THEME_NAME.DEFAULT);
const [lang, setLang] = useState<LANG_NAME>(LANG_NAME.ZH);
const [themes, setThemes] = useState(defaultAlgorithm);
const [myThemes, setMyThemes] = useState<DefaultTheme>(lightDefaultTheme);
const { i18n } = useTranslation();
const changeMode = (themeName: THEME_NAME) => {
if (themeName === THEME_NAME.DARK) {
darkAlgorithm.algorithm = theme.darkAlgorithm;
// for ant change mode
setThemes(darkAlgorithm);
// for custome use mode
setMyThemes(darkDefaultTheme);
} else {
defaultAlgorithm.algorithm = theme.defaultAlgorithm;
setThemes(defaultAlgorithm);
setMyThemes(lightDefaultTheme);
}
};
useEffect(() => {
changeMode(themeName);
}, [themeName]);
useEffect(() => {
// change lang
i18n.changeLanguage(lang);
}, [lang, i18n]);
return (
<MyContext.Provider value={{ themeName, lang, setThemeName, setLang }}>
<ConfigProvider locale={lang === LANG_NAME.ZH ? zhCN : enUS} theme={themes}>
<ThemeProvider theme={myThemes}>{children}</ThemeProvider>
</ConfigProvider>
</MyContext.Provider>
);
};

@ -1,17 +0,0 @@
import React, { createContext, useState, ReactNode } from 'react';
export enum THEME_NAME {
DEFAULT = 'default',
DARK = 'dark',
}
export const MyThemeContext = createContext<{ themeName: string; setThemeName: (name: THEME_NAME) => void } | null>(
null
);
export const ThemeStore: React.FC<{
children: ReactNode;
}> = ({ children }) => {
const [themeName, setThemeName] = useState<string>(THEME_NAME.DEFAULT);
return <MyThemeContext.Provider value={{ themeName, setThemeName }}>{children}</MyThemeContext.Provider>;
};

@ -1,16 +1,16 @@
import { useContext, useEffect } from 'react';
import { useLocalStorageState } from 'ahooks';
import { MyThemeContext, THEME_NAME } from '@/context/themeContext';
import { MyContext, THEME_NAME } from '@/context';
const useThemeMode = () => {
const useThemeMode = (): { isDark: boolean | undefined; setIsDark: (isDark: boolean) => void } => {
const [isDark, setIsDark] = useLocalStorageState<boolean>('current-mode', { defaultValue: false });
const { setThemeName } = useContext<any>(MyThemeContext);
const { setThemeName } = useContext<any>(MyContext);
useEffect(() => {
isDark ? setThemeName(THEME_NAME.DARK) : setThemeName(THEME_NAME.DEFAULT);
}, [isDark, setThemeName]);
return [setIsDark];
return { isDark, setIsDark };
};
export default useThemeMode;

@ -2,8 +2,8 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import { ThemeStore } from './context/themeContext';
import ThemeComponent from './components/theme-com';
import { MyStore } from './context';
import './config/i18n';
import 'antd/dist/reset.css';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
@ -12,12 +12,10 @@ root.render(
<React.StrictMode>
<BrowserRouter>
{/* theme context */}
<ThemeStore>
<MyStore>
{/* theme config context */}
<ThemeComponent>
<App />
</ThemeComponent>
</ThemeStore>
<App />
</MyStore>
</BrowserRouter>
</React.StrictMode>
);

@ -1,7 +1,10 @@
import { Button } from 'antd';
import { Button, Calendar } from 'antd';
import dayjs from 'dayjs';
import request from '@/utils';
import { useTranslation } from 'react-i18next';
const Home = () => {
const { t } = useTranslation();
const fetchdata = (body: { duid: string }) => {
return request<{ phone: string }>('https://mock.xiaojukeji.com/mock/16635/am/marketing/mis/member/archive/phone', {
method: 'post',
@ -11,7 +14,8 @@ const Home = () => {
return (
<div style={{ color: 'red' }}>
<Button onClick={() => fetchdata({ duid: '1234234' })}>jjjjj</Button>
<Button>{t('hello')}</Button>
<Calendar fullscreen={false} value={dayjs()} />
</div>
);
};

@ -1,47 +0,0 @@
import { theme } from 'antd';
import { lightDefaultTheme } from '.';
export const defaultAlgorithm = {
token: {
borderRadius: 2,
colorPrimary: lightDefaultTheme.primary,
fontSize: 14,
// colorBgBase: lightDefaultTheme.backgroundColor.bg1,
},
components: {
Layout: {
bodyBg: lightDefaultTheme.backgroundColor.bg1,
headerBg: lightDefaultTheme.backgroundColor.bgHeader,
},
Button: {
fontSize: 14,
},
Table: {
borderRadius: 0,
borderRadiusLG: 0,
padding: 10,
paddingXS: 5,
margin: 0,
fontSize: 14,
colorBorderSecondary: lightDefaultTheme.borderColor.bl1,
paddingContentVerticalLG: 4,
},
Modal: {
borderRadiusLG: 2,
borderRadiusSM: 2,
colorText: lightDefaultTheme.fontColor.fc3,
borderRadius: 2,
paddingContentHorizontalLG: 0,
paddingMD: 0,
},
Menu: {
itemBg: lightDefaultTheme.backgroundColor.bg1,
activeBarWidth: 0,
activeBarHeight: 0,
activeBarBorderWidth: 0,
subMenuItemBorderRadius: 8,
horizontalItemBorderRadius: 8,
itemBorderRadius: 8,
},
},
algorithm: theme.defaultAlgorithm,
};

@ -50,7 +50,7 @@
resolved "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.3.1.tgz#4b2f65a17d4d32b526baa6414aca2117382bf8da"
integrity sha512-4QBZg8ccyC6LPIRii7A0bZUk3+lEDCLnhB+FVsflGdcWPPmV+j3fire4AwwoqHV/BibgvBmR9ZIo4s867smv+g==
"@ant-design/icons@^5.2.2":
"@ant-design/icons@^5.2.2", "@ant-design/icons@^5.2.6":
version "5.2.6"
resolved "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.2.6.tgz#2d4a9a37f531eb2a20cebec01d6fb69cf593900d"
integrity sha512-4wn0WShF43TrggskBJPRqCD0fcHbzTYjnaoskdiJrVHg86yxoZ8ZUqsXvyn4WUqehRiFKnaclOhqk9w4Ui2KVw==
@ -1187,7 +1187,7 @@
resolved "https://registry.npmmirror.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.19.4", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.22.15"
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
@ -4147,7 +4147,7 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
dayjs@^1.11.1, dayjs@^1.9.1:
dayjs@^1.11.1, dayjs@^1.11.9, dayjs@^1.9.1:
version "1.11.9"
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a"
integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==
@ -5735,6 +5735,13 @@ husky@^8.0.0:
resolved "https://registry.npmmirror.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
i18next-browser-languagedetector@^7.1.0:
version "7.1.0"
resolved "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.1.0.tgz#01876fac51f86b78975e79b48ccb62e2313a2d7d"
integrity sha512-cr2k7u1XJJ4HTOjM9GyOMtbOA47RtUoWRAtt52z43r3AoMs2StYKyjS3URPhzHaf+mn10hY9dZWamga5WPQjhA==
dependencies:
"@babel/runtime" "^7.19.4"
i18next@^23.5.1:
version "23.5.1"
resolved "https://registry.npmmirror.com/i18next/-/i18next-23.5.1.tgz#7f7c35ffaa907618d9489f106d5006b09fbca3d3"

Loading…
Cancel
Save