Skip to content

Commit

Permalink
Add i18n
Browse files Browse the repository at this point in the history
  • Loading branch information
cnwhy committed Jun 13, 2024
1 parent d652eeb commit 2f84386
Show file tree
Hide file tree
Showing 40 changed files with 612 additions and 274 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ dist-ssr
*.sln
*.sw?

public/monaco-editor
public/monaco-editor
src/i18n/*.json
77 changes: 77 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@
"analyzer": "tsc && vite build --mode=analyzer",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"init": "npm run init:monaco && npm run init:i18n",
"init:i18n": "node ./script/language.js",
"init:monaco": "cpx ./node_modules/monaco-editor/min/** ./public/monaco-editor/min",
"predev": "npm run init:monaco"
"predev": "npm run init",
"prebuild": "npm run init:i18n",
"preanalyzer": "npm run init:i18n"
},
"dependencies": {
"@ant-design/icons": "^5.2.6",
"antd": "^5.11.0",
"axios": "^1.6.0",
"classnames": "^2.5.1",
"events": "^3.3.0",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^8.0.0",
"idb": "^7.1.1",
"idb-open-plus": "^1.0.0",
"jsonc-parser": "^3.2.0",
Expand All @@ -26,6 +32,7 @@
"qs": "^6.11.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^14.1.2",
"uuid": "^9.0.1"
},
"devDependencies": {
Expand All @@ -38,6 +45,7 @@
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.3",
"cpx2": "^7.0.1",
"csv-parse": "^5.5.6",
"eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
Expand Down
78 changes: 78 additions & 0 deletions script/language.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import fs from "fs";
import { writeFile } from "fs/promises";
import path from "path";
import url from "url";
import { parse } from "csv-parse";

const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const importFile = path.join(__dirname, "../src/i18n/languages.csv");
const exportDir = path.join(__dirname, "../src/i18n");

let languages;
const langMap = new Map();
const init = (arr) => {
const [_key, ...langs] = arr;
languages = langs.map((langName) => {
const lang = {};
langMap.set(langName, lang);
return lang;
});
};
const output = () => {
// 保存为json文件
const langList = [];
const arr = [];
langMap.forEach((json, lang) => {
langList.push({ lang, label: json.language });
arr.push(
writeFile(
path.join(exportDir, `${lang}.json`),
JSON.stringify(json, undefined, 4)
)
);
});
arr.push(
writeFile(
path.join(exportDir, `languages.json`),
JSON.stringify(langList, undefined, 4)
)
);
Promise.all(arr).then(() => console.log("done"));
};

const setValue = (obj, key, value) => {
if (!obj || !key) return;
const keys = key.split(".");
let _obj = obj;
while (keys.length > 1) {
const _key = keys.shift();
if (_obj[_key] === undefined) {
_obj = _obj[_key] = {};
} else if (typeof _obj[_key] === "object") {
_obj = _obj[_key];
} else {
console.error(`Key [${key}] error!`);
}
}
const _key = keys[0];
if (_obj[_key] !== undefined) {
console.error(`Key [${key}] repeat!`);
}
_obj[_key] = value || "";
};

function main(){
const csvp = parse();
csvp.on("end", output);
csvp.on("data", function (chunk) {
// 解析language.csv
if (!languages) return init(chunk); // 第一行为表头
const [key, ...values] = chunk;
values.forEach((value, index) => {
setValue(languages[index], key, value);
});
});
fs.createReadStream(importFile).pipe(csvp);
}

main();
14 changes: 13 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ import "./App.css";
import { configEvent } from "./uitls/events";

import zhCN from "antd/locale/zh_CN";
import enGB from "antd/locale/en_GB";
import { ServerComm } from "./api/local";
import Ctx from "./uitls/ctx";
import { useIsDark } from "./uitls/useTheme";
import './uitls/i18n.ts';
import { useTranslation } from "react-i18next";

const Manage = React.lazy(() => import("./Pages/Manage"));

Expand All @@ -45,6 +48,7 @@ const AntdGlobal: React.FC<{ children: React.ReactNode }> = (props) => {

function App() {
const info = useInfo();
const { t, i18n } = useTranslation();
// const [gostConfig, setGostConfig] = useState<any>(null);
// const [localConfig, setLocalConfig] = useState<any>(null);
const gostConfig = useServerConfig();
Expand All @@ -53,10 +57,12 @@ function App() {
// const [userTheme, setUserTheme] = useState<any>(null); // 用户主题
const [serverLoading, setServerLoading] = useState<any>(false);
const [localLoading, setLocalLoading] = useState<any>(false);
const [locale, setLocale] = useState<any>(zhCN);
const isLoading = useMemo(
() => serverLoading || localLoading,
[serverLoading, localLoading]
);

const slef = useRef({
update: async () => {
try {
Expand Down Expand Up @@ -137,6 +143,12 @@ function App() {
(window as any)?.monaco?.editor.setTheme("vs");
}
}, [isDark]);

useEffect(() => {
const lang = i18n.resolvedLanguage;
setLocale(lang === 'zh' ? zhCN : enGB);
},[i18n.resolvedLanguage])

return (
<Ctx.Provider
value={{
Expand All @@ -147,7 +159,7 @@ function App() {
>
<ConfigProvider
theme={{ algorithm: isDark ? theme.darkAlgorithm : undefined }}
locale={zhCN}
locale={locale}
>
<AntdGlobal>
<React.Suspense fallback="loading...">
Expand Down
32 changes: 17 additions & 15 deletions src/Pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React from "react";
import { GlobalOutlined, LockOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Checkbox, Form, Input } from "antd";
import { Button, Checkbox, Form, Input, Space } from "antd";
import { login } from "../uitls/server";
import LocalServers from "../components/LocalServers";
import { ThemeButton } from "../components/Theme";

import { useTranslation, Trans } from 'react-i18next';
import { LanguageButton } from "../components/Language";
const Home: React.FC = () => {
const { t, i18n } = useTranslation();
return (
<>
<Form
Expand Down Expand Up @@ -36,30 +38,27 @@ const Home: React.FC = () => {
);
}}
>
<h1>GOST API Manage</h1>
<h2>首先连接API服务</h2>
<h1>{t('base.title')}</h1>
<h2>{t('base.description')}</h2>
<Form.Item
name="baseURL"
rules={[
{
required: true,
message: "请输入API地址",
message: t('msg.baseURL.required'),
},
{
validator(rule, value, callback) {
if (value === "http://") {
callback("请输入API地址");
callback(t('msg.baseURL.required'));
}
callback();
},
},
// {
// type:'url'
// }
]}
>
<Input
placeholder="API baseURL"
placeholder={t('placeholder.baseURL')}
prefix={<GlobalOutlined className={"prefixIcon"} />}
></Input>
</Form.Item>
Expand All @@ -76,18 +75,21 @@ const Home: React.FC = () => {
></Input.Password>
</Form.Item>
<Form.Item name="save" valuePropName="checked">
<Checkbox>保存到本地</Checkbox>
<Checkbox>{t('base.form.local')}</Checkbox>
</Form.Item>
<Form.Item noStyle style={{ marginBottom: "1em" }}>
<Button block type="primary" htmlType="submit">
链接
{t('base.cmd.connect')}
</Button>
</Form.Item>
<LocalServers></LocalServers>
</Form>
<ThemeButton
style={{ position: "absolute", top: "1em", right: "1em" }}
></ThemeButton>
<div style={{ position: "absolute", top: "1em", right: "1em" }}>
<Space>
<ThemeButton />
<LanguageButton />
</Space>
</div>
</>
);
};
Expand Down
Loading

0 comments on commit 2f84386

Please sign in to comment.