Skip to content

Commit 893bf77

Browse files
committed
feat:新增多语言切换
1 parent 2d27389 commit 893bf77

File tree

10 files changed

+134
-58
lines changed

10 files changed

+134
-58
lines changed

website/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
"@uiw/react-codepen": "~1.0.2",
2323
"@uiw/copy-to-clipboard": "^1.0.12",
2424
"@wcj/dark-mode": "^1.0.14",
25+
"react-i18next": "~11.16.9",
26+
"i18next": "~21.7.1",
2527
"react": "18.1.0",
2628
"react-dom": "18.1.0",
2729
"react-router-dom": "^6.3.0",

website/src/Layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Outlet, NavLink } from 'react-router-dom';
33
import GitHubCorners from '@uiw/react-github-corners';
44
import '@wcj/dark-mode';
55
import styles from './Layout.module.less';
6-
6+
import Language from './language/Select';
77
export function Layout() {
88
return (
99
<Fragment>
@@ -17,6 +17,7 @@ export function Layout() {
1717
Example
1818
</NavLink>
1919
<dark-mode permanent></dark-mode>
20+
<Language />
2021
</nav>
2122
</header>
2223
<div className={styles.warpper}>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useEffect, useState } from 'react';
2+
import { CodeBlockData } from 'markdown-react-code-preview-loader';
3+
import { useTranslation } from 'react-i18next';
4+
5+
const useMdData = (path: (lang: string) => Promise<{ default: CodeBlockData }>) => {
6+
const init = useTranslation();
7+
const [mdData, setMdData] = useState<CodeBlockData>({
8+
source: '',
9+
components: {},
10+
codeBlock: {},
11+
languages: {},
12+
});
13+
const lang = init.t('language');
14+
useEffect(() => {
15+
const getMd = async () => {
16+
try {
17+
const result = await path(lang);
18+
if (result.default) {
19+
setMdData(result.default);
20+
}
21+
} catch (err) {
22+
console.warn(err);
23+
}
24+
};
25+
getMd();
26+
}, [lang, path]);
27+
return mdData;
28+
};
29+
30+
export default useMdData;

website/src/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom/client';
33
import App from './App';
4+
import './language/i18n';
45

56
ReactDOM.createRoot(document.getElementById('root')!).render(
67
<React.StrictMode>

website/src/language/Select.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import languageData from './language.json';
3+
import { Select } from 'uiw';
4+
import { useTranslation } from 'react-i18next';
5+
6+
const Language = () => {
7+
const { i18n } = useTranslation();
8+
const Options = React.useMemo(() => {
9+
return Object.entries(languageData).map(([key, item]) => {
10+
return (
11+
<Select.Option key={key} value={key}>
12+
{item.label}
13+
</Select.Option>
14+
);
15+
});
16+
// eslint-disable-next-line react-hooks/exhaustive-deps
17+
}, [JSON.stringify(languageData)]);
18+
19+
return (
20+
<div style={{ width: 100 }}>
21+
<Select
22+
value={i18n.language}
23+
onChange={(event) => {
24+
const value = event.target.value;
25+
i18n.changeLanguage(value);
26+
}}
27+
>
28+
{Options}
29+
</Select>
30+
</div>
31+
);
32+
};
33+
34+
export default Language;

website/src/language/i18n.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import i18n from 'i18next';
2+
import { initReactI18next } from 'react-i18next';
3+
import LanguageData from './language.json';
4+
// the translations
5+
// (tip move them in a JSON file and import them,
6+
// or even better, manage them separated from your code: https://react.i18next.com/guides/multiple-translation-files)
7+
const resources: Record<string, { translation: { language: string } }> = {};
8+
Object.entries(LanguageData).forEach(([key, item]) => {
9+
resources[key] = {
10+
translation: {
11+
language: item.value,
12+
},
13+
};
14+
});
15+
16+
i18n
17+
.use(initReactI18next) // passes i18n down to react-i18next
18+
.init({
19+
resources,
20+
lng: 'en', // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
21+
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
22+
// if you're using a language detector, do not define the lng option
23+
});
24+
25+
export default i18n;

website/src/language/language.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"zh": {
3+
"label": "中文",
4+
"value": "-zh"
5+
},
6+
"en": {
7+
"label": "英文",
8+
"value": ""
9+
}
10+
}

website/src/pages/docs/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import MarkdownPreview from '@uiw/react-markdown-preview';
2-
import mdStr from 'markdown-react-code-preview-loader/README.md';
3-
2+
import useMdData from './../../components/useMdData';
43
export function HomePage() {
5-
return <MarkdownPreview source={mdStr.source} />;
4+
const mdData = useMdData((lang) => import(`markdown-react-code-preview-loader/README${lang}.md`));
5+
return <MarkdownPreview source={mdData.source} />;
66
}

website/src/pages/example/App.md

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
# Alert 确认对话框
1+
# Alert Confirmation dialog box
22

33
[![Open in unpkg](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@uiw/react-alert/file/README.md)
44
[![NPM Downloads](https://img.shields.io/npm/dm/@uiw/react-alert.svg?style=flat)](https://www.npmjs.com/package/@uiw/react-alert)
55
[![npm version](https://img.shields.io/npm/v/@uiw/react-alert.svg?label=@uiw/react-alert)](https://npmjs.com/@uiw/react-alert)
66

7-
弹出对话框会在继续之前,通知用户重要信息,点击确定后异步关闭对话框。
7+
The pop-up dialog box will notify the user of important information before continuing, and click OK to close the dialog box asynchronously.
88

99
```js
1010
import { Alert } from "uiw";
1111
// or
1212
import Alert from "@uiw/react-alert";
1313
```
1414

15-
## 基本用法
15+
## Basic Usage
1616

1717
```jsx mdx:preview
1818
import React from "react";
@@ -70,9 +70,9 @@ class Demo extends React.Component {
7070
export default Demo;
7171
```
7272

73-
## 延迟关闭对话框
73+
## Delay closing dialog
7474

75-
这里是利用 `Promise` 等它执行完成再去关闭窗口
75+
Here is to use `Promise` to close the window after its execution is completed
7676

7777
```jsx mdx:preview
7878
import React from "react";
@@ -134,9 +134,9 @@ class Demo extends React.Component {
134134
export default Demo;
135135
```
136136

137-
## 带图标的弹出框
137+
## Pop up box with Icon
138138

139-
通过设置 `icon` `type` 参数设置带状态的弹出对话框。
139+
Set the pop-up dialog box with status by setting the `icon` and `type` parameters.
140140

141141
```jsx mdx:preview
142142
import React from "react";
@@ -242,13 +242,7 @@ class Demo extends React.Component {
242242
export default Demo;
243243
```
244244

245-
```js
246-
import { Alert } from "uiw";
247-
// or
248-
import Alert from "@uiw/react-alert";
249-
```
250-
251-
## 带标题的弹出框
245+
## Pop up box with title
252246

253247
```jsx mdx:preview
254248
import React from "react";
@@ -359,9 +353,9 @@ class Demo extends React.Component {
359353
export default Demo;
360354
```
361355

362-
### 自定义按钮
356+
### Custom button
363357

364-
这个对话框有两个按钮,单击 **`确定按钮`** **`取消按钮`** 后,此对话框将关闭,将不触发 ~~`onConfirm`~~ ~~`onCancel`~~ 事件。因为这俩按钮是自定义按钮。你可以正对自定义按钮外面的对象定义 `className="w-alert-footer"` 将显示默认样式。
358+
There are two buttons in this dialog box. After clicking **OK button** or **cancel button**, this dialog box will close and the ~~`onconfirm`~~ or ~~`OnCancel`~~ event will not be triggered. Because these two buttons are custom buttons. You can define `classname = "w-alert-footer"` for the object outside the custom button, and the default style will be displayed.
365359

366360
```jsx mdx:preview:base23
367361
import React from "react";
@@ -415,7 +409,7 @@ class Demo extends React.Component {
415409
export default Demo;
416410
```
417411

418-
### 不显示遮罩层
412+
### Mask layers are not displayed
419413

420414
```jsx mdx:preview
421415
import React from "react";
@@ -463,19 +457,18 @@ export default Demo;
463457

464458
## Props
465459

466-
虽然类似于对 `<Modal>`,但 `<Alert>` 更具限制性,只应用于重要信息。此组件继承 [`<Modal>`](#/components/modal) 的属性,所以部分参数可以参考 `<Modal>` 组件。
467-
468-
| 参数 | 说明 | 类型 | 默认值 |
469-
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ------- |
470-
| onCancel | 取消按钮的回调函数 | Function(e) | - |
471-
| onConfirm | 点击确定按钮回调 | Function(e) | - |
472-
| cancelText | 取消按钮文字, | String | - |
473-
| confirmText | 确认按钮文字 | String | `确认` |
474-
| type | 按钮类型跟 `<Button>` 组件的 `type` 参数一致,同时会影响按钮颜色。 | String | `light` |
475-
| icon[`<Modal>`](#/components/modal) | 设置对话框右上角图标,,设置 `type` 将图标设置不同的颜色。当前属性为 [`<Icon>`](#/components/icon) 组件的 `type` 属性,所以可以参考该组件自定义图标。 | String/ReactNode | - |
476-
| title[`<Modal>`](#/components/modal) | 设置标题 | Function(e) | - |
477-
| useButton[`<Modal>`](#/components/modal) | 是否使用默认按钮,如果设置 `false` 需要自定义按钮关闭 | Boolean | `true` |
478-
| isOpen[`<Modal>`](#/components/modal) | 对话框是否可见 | Boolean | `false` |
479-
| maskClosable[`<Modal>`](#/components/modal) | 点击遮罩层是否允许关闭 | boolean | `true` |
460+
Although similar to `<Modal>`, `<Alert>` is more restrictive and applies only to important information. This component inherits the properties of [`<Modal>`](#/components/modal), so some parameters can refer to the `<Modal>` component.
461+
| parameter | explain | type | Default value |
462+
| ------------------------------------------- | ----- | ---------------- | ------- |
463+
| onCancel | Callback function of cancel button | Function(e) | - |
464+
| onConfirm | Click OK to call back | Function(e) | - |
465+
| cancelText | Cancel button text, | String | - |
466+
| confirmText | Confirm button text | String | `确认` |
467+
| type | The button type is consistent with the `type` parameter of the `<Button>` component and will affect the button color.| String | `light` |
468+
| icon[`<Modal>`](#/components/modal) | Set the icon in the upper right corner of the dialog box. Set `type` to set the icon to a different color. The current attribute is the `type` attribute of [`<Icon>`](#/components/icon) component, so you can refer to the custom icon of the component. | String/ReactNode | - |
469+
| title[`<Modal>`](#/components/modal) | Set title | Function(e) | - |
470+
| useButton[`<Modal>`](#/components/modal) | Whether to use the default button. If you set `false`, you need to customize the button to close | Boolean | `true` |
471+
| isOpen[`<Modal>`](#/components/modal) | Whether the dialog box is visible | Boolean | `false` |
472+
| maskClosable[`<Modal>`](#/components/modal) | Click whether the mask layer is allowed to be turned off | boolean | `true` |
480473

481-
更多属性文档请参考 [Modal](#/components/modal)
474+
For more attribute documents, please refer to [Modal](#/components/modal)

website/src/pages/example/index.tsx

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { useEffect, useState } from 'react';
21
import MarkdownPreview from '@uiw/react-markdown-preview';
3-
import { CodeBlockData } from 'markdown-react-code-preview-loader';
42
import PreView from '../../components/CodeLayout';
5-
3+
import useMdData from './../../components/useMdData';
64
const getMetaData = (meta: string) => {
75
if (meta) {
86
const [metaItem] = /mdx:(.[\w|:]+)/i.exec(meta) || [];
@@ -16,25 +14,7 @@ const getMetaData = (meta: string) => {
1614
};
1715

1816
export function ExamplePage() {
19-
const [mdData, setMdData] = useState<CodeBlockData>({
20-
source: '',
21-
components: {},
22-
codeBlock: {},
23-
languages: {},
24-
});
25-
26-
const [lang] = useState('');
27-
useEffect(() => {
28-
const getMd = async () => {
29-
// const result = await import(`@uiw/react-layout/README${lang}.md`);
30-
const result = await import(`./App${lang}.md`);
31-
console.log(result);
32-
if (result.default) {
33-
setMdData(result.default);
34-
}
35-
};
36-
getMd();
37-
}, [lang]);
17+
const mdData = useMdData((lang) => import(`./App${lang}.md`));
3818
return (
3919
<div>
4020
<MarkdownPreview

0 commit comments

Comments
 (0)