diff --git a/package.json b/package.json
index 5c08f03a0c419643cee09eae3d514a98ea9ebe4b..2439d87eab51ce25d37203e1ee05e0d41d052c94 100644
--- a/package.json
+++ b/package.json
@@ -23,8 +23,10 @@
     "ahooks": "^3.1.13",
     "axios": "^0.26.0",
     "dayjs": "^1.10.8",
+    "i18next": "^21.6.14",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
+    "react-i18next": "^11.15.6",
     "react-router-dom": "^6.2.2",
     "react-virtuoso": "^2.7.0",
     "recoil": "^0.6.1",
diff --git a/src-tauri/src/core/verge.rs b/src-tauri/src/core/verge.rs
index 01fad6086310ce9c8f705e3696f50253bee63973..615f9174009d9865b276628b6eadb47e76fa7e5b 100644
--- a/src-tauri/src/core/verge.rs
+++ b/src-tauri/src/core/verge.rs
@@ -1,6 +1,6 @@
+use crate::log_if_err;
 use crate::{
   core::Clash,
-  log_if_err,
   utils::{config, dirs, sysopt::SysProxyConfig},
 };
 use anyhow::{bail, Result};
@@ -12,6 +12,9 @@ use tauri::{async_runtime::Mutex, utils::platform::current_exe};
 /// ### `verge.yaml` schema
 #[derive(Default, Debug, Clone, Deserialize, Serialize)]
 pub struct VergeConfig {
+  // i18n
+  pub language: Option<String>,
+
   /// `light` or `dark`
   pub theme_mode: Option<String>,
 
@@ -188,6 +191,9 @@ impl Verge {
   /// so call the save_file at the end is savely
   pub fn patch_config(&mut self, patch: VergeConfig) -> Result<()> {
     // only change it
+    if patch.language.is_some() {
+      self.config.language = patch.language;
+    }
     if patch.theme_mode.is_some() {
       self.config.theme_mode = patch.theme_mode;
     }
diff --git a/src-tauri/src/utils/tmpl.rs b/src-tauri/src/utils/tmpl.rs
index 979ff86e6f99addc9e108a05509981daa66edde9..bf7aceede75715507182d5e5df37ec0b3c08c639 100644
--- a/src-tauri/src/utils/tmpl.rs
+++ b/src-tauri/src/utils/tmpl.rs
@@ -21,6 +21,7 @@ items: ~
 /// template for `verge.yaml`
 pub const VERGE_CONFIG: &[u8] = b"# Defaulf Config For Clash Verge
 
+language: en
 theme_mode: light
 theme_blur: false
 traffic_graph: true
diff --git a/src/components/setting/setting-clash.tsx b/src/components/setting/setting-clash.tsx
index 7ae9dd19335f1fbe2e0dafb72e022386de1057ad..5106e6fab97de2ddf472a943f4dd8d94dbd239f5 100644
--- a/src/components/setting/setting-clash.tsx
+++ b/src/components/setting/setting-clash.tsx
@@ -1,5 +1,6 @@
 import useSWR, { useSWRConfig } from "swr";
 import { useSetRecoilState } from "recoil";
+import { useTranslation } from "react-i18next";
 import {
   ListItemText,
   TextField,
@@ -21,15 +22,16 @@ interface Props {
 }
 
 const SettingClash = ({ onError }: Props) => {
+  const { t } = useTranslation();
   const { mutate } = useSWRConfig();
   const { data: clashConfig } = useSWR("getClashConfig", getClashConfig);
   const { data: versionData } = useSWR("getVersion", getVersion);
 
   const {
-    ipv6 = false,
-    "allow-lan": allowLan = false,
-    "log-level": logLevel = "silent",
-    "mixed-port": mixedPort = 0,
+    ipv6,
+    "allow-lan": allowLan,
+    "log-level": logLevel,
+    "mixed-port": mixedPort,
   } = clashConfig ?? {};
 
   const setGlobalClashPort = useSetRecoilState(atomClashPort);
@@ -64,11 +66,11 @@ const SettingClash = ({ onError }: Props) => {
     : versionData?.version || "-";
 
   return (
-    <SettingList title="Clash Setting">
+    <SettingList title={t("Clash Setting")}>
       <SettingItem>
-        <ListItemText primary="Allow Lan" />
+        <ListItemText primary={t("Allow Lan")} />
         <GuardState
-          value={allowLan}
+          value={allowLan ?? false}
           valueProps="checked"
           onCatch={onError}
           onFormat={onSwitchFormat}
@@ -80,9 +82,9 @@ const SettingClash = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="IPv6" />
+        <ListItemText primary={t("IPv6")} />
         <GuardState
-          value={ipv6}
+          value={ipv6 ?? false}
           valueProps="checked"
           onCatch={onError}
           onFormat={onSwitchFormat}
@@ -94,9 +96,9 @@ const SettingClash = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Log Level" />
+        <ListItemText primary={t("Log Level")} />
         <GuardState
-          value={logLevel}
+          value={logLevel ?? "info"}
           onCatch={onError}
           onFormat={(e: any) => e.target.value}
           onChange={(e) => onChangeData({ "log-level": e })}
@@ -113,9 +115,9 @@ const SettingClash = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Mixed Port" />
+        <ListItemText primary={t("Mixed Port")} />
         <GuardState
-          value={mixedPort!}
+          value={mixedPort ?? 0}
           onCatch={onError}
           onFormat={(e: any) => +e.target.value?.replace(/\D+/, "")}
           onChange={(e) => onChangeData({ "mixed-port": e })}
@@ -127,7 +129,7 @@ const SettingClash = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Clash core" />
+        <ListItemText primary={t("Clash core")} />
         <Typography sx={{ py: 1 }}>{clashVer}</Typography>
       </SettingItem>
     </SettingList>
diff --git a/src/components/setting/setting-system.tsx b/src/components/setting/setting-system.tsx
index dfc482ec4b5d2f1bfbbb8f933c0f4417b76381ea..a09b3b432ef40bd51c7c07fb83232af9d4e65056 100644
--- a/src/components/setting/setting-system.tsx
+++ b/src/components/setting/setting-system.tsx
@@ -1,4 +1,5 @@
 import useSWR, { useSWRConfig } from "swr";
+import { useTranslation } from "react-i18next";
 import { Box, ListItemText, Switch, TextField } from "@mui/material";
 import { getVergeConfig, patchVergeConfig } from "../../services/cmds";
 import { SettingList, SettingItem } from "./setting";
@@ -11,15 +12,16 @@ interface Props {
 }
 
 const SettingSystem = ({ onError }: Props) => {
+  const { t } = useTranslation();
   const { mutate } = useSWRConfig();
   const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
 
   const {
-    enable_tun_mode = false,
-    enable_auto_launch = false,
-    enable_system_proxy = false,
-    system_proxy_bypass = "",
-    enable_proxy_guard = false,
+    enable_tun_mode,
+    enable_auto_launch,
+    enable_system_proxy,
+    system_proxy_bypass,
+    enable_proxy_guard,
   } = vergeConfig ?? {};
 
   const onSwitchFormat = (_e: any, value: boolean) => value;
@@ -28,11 +30,11 @@ const SettingSystem = ({ onError }: Props) => {
   };
 
   return (
-    <SettingList title="System Setting">
+    <SettingList title={t("System Setting")}>
       <SettingItem>
-        <ListItemText primary="Tun Mode" />
+        <ListItemText primary={t("Tun Mode")} />
         <GuardState
-          value={enable_tun_mode}
+          value={enable_tun_mode ?? false}
           valueProps="checked"
           onCatch={onError}
           onFormat={onSwitchFormat}
@@ -44,9 +46,9 @@ const SettingSystem = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Auto Launch" />
+        <ListItemText primary={t("Auto Launch")} />
         <GuardState
-          value={enable_auto_launch}
+          value={enable_auto_launch ?? false}
           valueProps="checked"
           onCatch={onError}
           onFormat={onSwitchFormat}
@@ -61,13 +63,13 @@ const SettingSystem = ({ onError }: Props) => {
         <ListItemText
           primary={
             <Box sx={{ display: "flex", alignItems: "center" }}>
-              System Proxy
+              {t("System Proxy")}
               <SysproxyTooltip />
             </Box>
           }
         />
         <GuardState
-          value={enable_system_proxy}
+          value={enable_system_proxy ?? false}
           valueProps="checked"
           onCatch={onError}
           onFormat={onSwitchFormat}
@@ -83,9 +85,9 @@ const SettingSystem = ({ onError }: Props) => {
 
       {enable_system_proxy && (
         <SettingItem>
-          <ListItemText primary="Proxy Guard" />
+          <ListItemText primary={t("Proxy Guard")} />
           <GuardState
-            value={enable_proxy_guard}
+            value={enable_proxy_guard ?? false}
             valueProps="checked"
             onCatch={onError}
             onFormat={onSwitchFormat}
@@ -99,7 +101,7 @@ const SettingSystem = ({ onError }: Props) => {
 
       {enable_system_proxy && (
         <SettingItem>
-          <ListItemText primary="Proxy Bypass" />
+          <ListItemText primary={t("Proxy Bypass")} />
           <GuardState
             value={system_proxy_bypass ?? ""}
             onCatch={onError}
diff --git a/src/components/setting/setting-verge.tsx b/src/components/setting/setting-verge.tsx
index 8a5470fe62039c4920a37bf95f494ef96c462785..19f19cd4331977883f177c4711ad1401d6b887d1 100644
--- a/src/components/setting/setting-verge.tsx
+++ b/src/components/setting/setting-verge.tsx
@@ -1,5 +1,13 @@
 import useSWR, { useSWRConfig } from "swr";
-import { IconButton, ListItemText, Switch, Typography } from "@mui/material";
+import { useTranslation } from "react-i18next";
+import {
+  IconButton,
+  ListItemText,
+  MenuItem,
+  Select,
+  Switch,
+  Typography,
+} from "@mui/material";
 import {
   getVergeConfig,
   openAppDir,
@@ -18,11 +26,11 @@ interface Props {
 }
 
 const SettingVerge = ({ onError }: Props) => {
+  const { t } = useTranslation();
   const { mutate } = useSWRConfig();
   const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
 
-  const { theme_mode = "light", theme_blur = false, traffic_graph } =
-    vergeConfig ?? {};
+  const { theme_mode, theme_blur, traffic_graph, language } = vergeConfig ?? {};
 
   const onSwitchFormat = (_e: any, value: boolean) => value;
   const onChangeData = (patch: Partial<CmdType.VergeConfig>) => {
@@ -30,9 +38,9 @@ const SettingVerge = ({ onError }: Props) => {
   };
 
   return (
-    <SettingList title="Verge Setting">
+    <SettingList title={t("Verge Setting")}>
       <SettingItem>
-        <ListItemText primary="Theme Mode" />
+        <ListItemText primary={t("Theme Mode")} />
         <GuardState
           value={theme_mode === "dark"}
           valueProps="checked"
@@ -48,9 +56,9 @@ const SettingVerge = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Theme Blur" />
+        <ListItemText primary={t("Theme Blur")} />
         <GuardState
-          value={theme_blur}
+          value={theme_blur ?? false}
           valueProps="checked"
           onCatch={onError}
           onFormat={onSwitchFormat}
@@ -62,7 +70,7 @@ const SettingVerge = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Traffic Graph" />
+        <ListItemText primary={t("Traffic Graph")} />
         <GuardState
           value={traffic_graph ?? true}
           valueProps="checked"
@@ -76,21 +84,37 @@ const SettingVerge = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Open App Dir" />
+        <ListItemText primary={t("Language")} />
+        <GuardState
+          value={language ?? "en"}
+          onCatch={onError}
+          onFormat={(e: any) => e.target.value}
+          onChange={(e) => onChangeData({ language: e })}
+          onGuard={(e) => patchVergeConfig({ language: e })}
+        >
+          <Select size="small" sx={{ width: 100 }}>
+            <MenuItem value="zh">中文</MenuItem>
+            <MenuItem value="en">English</MenuItem>
+          </Select>
+        </GuardState>
+      </SettingItem>
+
+      <SettingItem>
+        <ListItemText primary={t("Open App Dir")} />
         <IconButton color="inherit" size="small" onClick={openAppDir}>
           <ArrowForward />
         </IconButton>
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Open Logs Dir" />
+        <ListItemText primary={t("Open Logs Dir")} />
         <IconButton color="inherit" size="small" onClick={openLogsDir}>
           <ArrowForward />
         </IconButton>
       </SettingItem>
 
       <SettingItem>
-        <ListItemText primary="Version" />
+        <ListItemText primary={t("Version")} />
         <Typography sx={{ py: "6px" }}>v{version}</Typography>
       </SettingItem>
     </SettingList>
diff --git a/src/locales/en.json b/src/locales/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..aba0ea191256a6f257c7e07bd926058ca4c6c92f
--- /dev/null
+++ b/src/locales/en.json
@@ -0,0 +1,42 @@
+{
+  "Label-Proxies": "Proxies",
+  "Label-Profiles": "Profiles",
+  "Label-Connections": "Connections",
+  "Label-Logs": "Logs",
+  "Label-Settings": "Settings",
+
+  "Connections": "Connections",
+  "Logs": "Logs",
+  "Clear": "Clear",
+  "Proxies": "Proxies",
+  "Proxy Groups": "Proxy Groups",
+  "rule": "rule",
+  "global": "global",
+  "direct": "direct",
+  "Profiles": "Profiles",
+  "Profile URL": "Profile URL",
+  "Import": "Import",
+  "New": "New",
+
+  "Settings": "Settings",
+  "Clash Setting": "Clash Setting",
+  "System Setting": "System Setting",
+  "Verge Setting": "Verge Setting",
+  "Allow Lan": "Allow Lan",
+  "IPv6": "IPv6",
+  "Log Level": "Log Level",
+  "Mixed Port": "Mixed Port",
+  "Clash core": "Clash core",
+  "Tun Mode": "Tun Mode",
+  "Auto Launch": "Auto Launch",
+  "System Proxy": "System Proxy",
+  "Proxy Guard": "Proxy Guard",
+  "Proxy Bypass": "Proxy Bypass",
+  "Theme Mode": "Theme Mode",
+  "Theme Blur": "Theme Blur",
+  "Traffic Graph": "Traffic Graph",
+  "Language": "Language",
+  "Open App Dir": "Open App Dir",
+  "Open Logs Dir": "Open Logs Dir",
+  "Version": "Version"
+}
diff --git a/src/locales/zh.json b/src/locales/zh.json
new file mode 100644
index 0000000000000000000000000000000000000000..a51b13264bf0a7cfaf0d608e9d0c0d00c3957a9a
--- /dev/null
+++ b/src/locales/zh.json
@@ -0,0 +1,42 @@
+{
+  "Label-Proxies": "代 理",
+  "Label-Profiles": "配 置",
+  "Label-Connections": "连 接",
+  "Label-Logs": "æ—¥ å¿—",
+  "Label-Settings": "设 置",
+
+  "Connections": "连接",
+  "Logs": "日志",
+  "Clear": "清除",
+  "Proxies": "代理",
+  "Proxy Groups": "代理组",
+  "rule": "规则",
+  "global": "全局",
+  "direct": "ç›´è¿ž",
+  "Profiles": "配置",
+  "Profile URL": "配置文件链接",
+  "Import": "导入",
+  "New": "新建",
+
+  "Settings": "设置",
+  "Clash Setting": "Clash 设置",
+  "System Setting": "系统设置",
+  "Verge Setting": "Verge 设置",
+  "Allow Lan": "局域网连接",
+  "IPv6": "IPv6",
+  "Log Level": "日志等级",
+  "Mixed Port": "端口设置",
+  "Clash core": "Clash 内核",
+  "Tun Mode": "Tun 模式",
+  "Auto Launch": "开机自启",
+  "System Proxy": "系统代理",
+  "Proxy Guard": "系统代理守卫",
+  "Proxy Bypass": "Proxy Bypass",
+  "Theme Mode": "暗夜模式",
+  "Theme Blur": "背景模糊",
+  "Traffic Graph": "流量图显",
+  "Language": "语言设置",
+  "Open App Dir": "应用目录",
+  "Open Logs Dir": "日志目录",
+  "Version": "版本"
+}
diff --git a/src/main.tsx b/src/main.tsx
index 7845c708374e3f05a8bebef83c56aa513813ad0d..849f898896fee550dce2286510771f5d63f9493a 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -7,6 +7,7 @@ import { RecoilRoot } from "recoil";
 import { BrowserRouter } from "react-router-dom";
 import Layout from "./pages/_layout";
 import enhance from "./services/enhance";
+import "./services/i18n";
 
 enhance.setup();
 
diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx
index e54b4f4b8cb99758840340441115fd9641b7e22d..46d3a9d4ce874580df3a4254343de39fb10f7526 100644
--- a/src/pages/_layout.tsx
+++ b/src/pages/_layout.tsx
@@ -1,5 +1,7 @@
+import i18next from "i18next";
 import useSWR, { SWRConfig, useSWRConfig } from "swr";
 import { useEffect, useMemo } from "react";
+import { useTranslation } from "react-i18next";
 import { Route, Routes } from "react-router-dom";
 import { alpha, createTheme, List, Paper, ThemeProvider } from "@mui/material";
 import { listen } from "@tauri-apps/api/event";
@@ -16,6 +18,7 @@ import UpdateButton from "../components/layout/update-button";
 const isMacos = navigator.userAgent.includes("Mac OS X");
 
 const Layout = () => {
+  const { t } = useTranslation();
   const { mutate } = useSWRConfig();
   const { data } = useSWR("getVergeConfig", getVergeConfig);
 
@@ -37,6 +40,12 @@ const Layout = () => {
     });
   }, []);
 
+  useEffect(() => {
+    if (data?.language) {
+      i18next.changeLanguage(data.language);
+    }
+  }, [data?.language]);
+
   const theme = useMemo(() => {
     // const background = mode === "light" ? "#f5f5f5" : "#000";
     const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5";
@@ -87,7 +96,7 @@ const Layout = () => {
             <List className="the-menu" data-windrag>
               {routers.map((router) => (
                 <LayoutItem key={router.label} to={router.link}>
-                  {router.label}
+                  {t(router.label)}
                 </LayoutItem>
               ))}
             </List>
diff --git a/src/pages/_routers.tsx b/src/pages/_routers.tsx
index f7d2f7594994fd205f8dc96c7c80f1e222e159a7..9ab46205a14ad1904d695215d02c98cec424c1a3 100644
--- a/src/pages/_routers.tsx
+++ b/src/pages/_routers.tsx
@@ -6,27 +6,27 @@ import ConnectionsPage from "./connections";
 
 export const routers = [
   {
-    label: "Proxies",
+    label: "Label-Proxies",
     link: "/",
     ele: ProxiesPage,
   },
   {
-    label: "Profiles",
+    label: "Label-Profiles",
     link: "/profile",
     ele: ProfilesPage,
   },
   {
-    label: "Connections",
+    label: "Label-Connections",
     link: "/connections",
     ele: ConnectionsPage,
   },
   {
-    label: "Logs",
+    label: "Label-Logs",
     link: "/logs",
     ele: LogsPage,
   },
   {
-    label: "Settings",
+    label: "Label-Settings",
     link: "/settings",
     ele: SettingsPage,
   },
diff --git a/src/pages/connections.tsx b/src/pages/connections.tsx
index 603f1504af7f0d8d9eb2d844fa09a69b5979636c..16ec9367d4f75e0b18bc23c9d94690c6a777d34a 100644
--- a/src/pages/connections.tsx
+++ b/src/pages/connections.tsx
@@ -1,6 +1,7 @@
 import { useEffect, useState } from "react";
 import { Paper } from "@mui/material";
 import { Virtuoso } from "react-virtuoso";
+import { useTranslation } from "react-i18next";
 import { ApiType } from "../services/types";
 import { getInfomation } from "../services/api";
 import BasePage from "../components/base/base-page";
@@ -8,6 +9,8 @@ import ConnectionItem from "../components/connection/connection-item";
 
 const ConnectionsPage = () => {
   const initConn = { uploadTotal: 0, downloadTotal: 0, connections: [] };
+
+  const { t } = useTranslation();
   const [conn, setConn] = useState<ApiType.Connections>(initConn);
 
   useEffect(() => {
@@ -27,7 +30,7 @@ const ConnectionsPage = () => {
   }, []);
 
   return (
-    <BasePage title="Connections" contentStyle={{ height: "100%" }}>
+    <BasePage title={t("Connections")} contentStyle={{ height: "100%" }}>
       <Paper sx={{ boxShadow: 2, height: "100%" }}>
         <Virtuoso
           data={conn.connections}
diff --git a/src/pages/logs.tsx b/src/pages/logs.tsx
index 78b1f826a78ef408ff3838ec028768228bf422a8..020f3ca43023316d9709d6d5b736f163d260d28a 100644
--- a/src/pages/logs.tsx
+++ b/src/pages/logs.tsx
@@ -1,16 +1,18 @@
 import { useRecoilState } from "recoil";
 import { Button, Paper } from "@mui/material";
 import { Virtuoso } from "react-virtuoso";
+import { useTranslation } from "react-i18next";
 import { atomLogData } from "../services/states";
 import BasePage from "../components/base/base-page";
 import LogItem from "../components/log/log-item";
 
 const LogPage = () => {
+  const { t } = useTranslation();
   const [logData, setLogData] = useRecoilState(atomLogData);
 
   return (
     <BasePage
-      title="Logs"
+      title={t("Logs")}
       contentStyle={{ height: "100%" }}
       header={
         <Button
@@ -19,7 +21,7 @@ const LogPage = () => {
           variant="contained"
           onClick={() => setLogData([])}
         >
-          Clear
+          {t("Clear")}
         </Button>
       }
     >
diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx
index e138e526aa709efde99b262f27e22e4a48191a6a..5cd331d66f170a0e3304d7de59ec47ee4e49c20f 100644
--- a/src/pages/profiles.tsx
+++ b/src/pages/profiles.tsx
@@ -2,6 +2,7 @@ import useSWR, { useSWRConfig } from "swr";
 import { useLockFn } from "ahooks";
 import { useEffect, useMemo, useState } from "react";
 import { Box, Button, Grid, TextField } from "@mui/material";
+import { useTranslation } from "react-i18next";
 import {
   getProfiles,
   patchProfile,
@@ -19,6 +20,7 @@ import ProfileItem from "../components/profile/profile-item";
 import ProfileMore from "../components/profile/profile-more";
 
 const ProfilePage = () => {
+  const { t } = useTranslation();
   const { mutate } = useSWRConfig();
 
   const [url, setUrl] = useState("");
@@ -175,12 +177,12 @@ const ProfilePage = () => {
   });
 
   return (
-    <BasePage title="Profiles">
+    <BasePage title={t("Profiles")}>
       <Box sx={{ display: "flex", mb: 2.5 }}>
         <TextField
           id="clas_verge_profile_url"
           name="profile_url"
-          label="Profile URL"
+          label={t("Profile URL")}
           size="small"
           fullWidth
           value={url}
@@ -193,10 +195,10 @@ const ProfilePage = () => {
           onClick={onImport}
           sx={{ mr: 1 }}
         >
-          Import
+          {t("Import")}
         </Button>
         <Button variant="contained" onClick={() => setDialogOpen(true)}>
-          New
+          {t("New")}
         </Button>
       </Box>
 
diff --git a/src/pages/proxies.tsx b/src/pages/proxies.tsx
index 20f69359bc70e49fd4f9fff0d407eaa20cd0e597..0df7cd1e33760705ba5c09dab9215a337a4fc461 100644
--- a/src/pages/proxies.tsx
+++ b/src/pages/proxies.tsx
@@ -1,6 +1,7 @@
 import useSWR, { useSWRConfig } from "swr";
 import { useEffect } from "react";
 import { useLockFn } from "ahooks";
+import { useTranslation } from "react-i18next";
 import { Button, ButtonGroup, List, Paper } from "@mui/material";
 import { getClashConfig, updateConfigs } from "../services/api";
 import { patchClashConfig } from "../services/cmds";
@@ -10,6 +11,7 @@ import ProxyGroup from "../components/proxy/proxy-group";
 import ProxyGlobal from "../components/proxy/proxy-global";
 
 const ProxyPage = () => {
+  const { t } = useTranslation();
   const { mutate } = useSWRConfig();
   const { data: proxiesData } = useSWR("getProxies", getProxies);
   const { data: clashConfig } = useSWR("getClashConfig", getClashConfig);
@@ -45,7 +47,7 @@ const ProxyPage = () => {
   return (
     <BasePage
       contentStyle={pageStyle}
-      title={showGroup ? "Proxy Groups" : "Proxies"}
+      title={showGroup ? t("Proxy Groups") : t("Proxies")}
       header={
         <ButtonGroup size="small">
           {modeList.map((mode) => (
@@ -55,7 +57,7 @@ const ProxyPage = () => {
               onClick={() => onChangeMode(mode)}
               sx={{ textTransform: "capitalize" }}
             >
-              {mode}
+              {t(mode)}
             </Button>
           ))}
         </ButtonGroup>
diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx
index a7aea421a9e6959831d809e6997b74edf165c8cb..e8c24463126c202eb80e02897499b9a78e76e678 100644
--- a/src/pages/settings.tsx
+++ b/src/pages/settings.tsx
@@ -1,4 +1,5 @@
 import { Paper } from "@mui/material";
+import { useTranslation } from "react-i18next";
 import Notice from "../components/base/base-notice";
 import BasePage from "../components/base/base-page";
 import SettingVerge from "../components/setting/setting-verge";
@@ -6,12 +7,14 @@ import SettingClash from "../components/setting/setting-clash";
 import SettingSystem from "../components/setting/setting-system";
 
 const SettingPage = () => {
-  const onError = (error: any) => {
-    error && Notice.error(error.toString());
+  const { t } = useTranslation();
+
+  const onError = (err: any) => {
+    Notice.error(err?.message || err.toString());
   };
 
   return (
-    <BasePage title="Settings">
+    <BasePage title={t("Settings")}>
       <Paper sx={{ borderRadius: 1, boxShadow: 2, mb: 3 }}>
         <SettingClash onError={onError} />
       </Paper>
diff --git a/src/services/i18n.ts b/src/services/i18n.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e3d8d3112fc8f8351a6f3b3e1f77ecd5edcc43fe
--- /dev/null
+++ b/src/services/i18n.ts
@@ -0,0 +1,17 @@
+import i18n from "i18next";
+import { initReactI18next } from "react-i18next";
+import en from "../locales/en.json";
+import zh from "../locales/zh.json";
+
+const resources = {
+  en: { translation: en },
+  zh: { translation: zh },
+};
+
+i18n.use(initReactI18next).init({
+  resources,
+  lng: "en",
+  interpolation: {
+    escapeValue: false,
+  },
+});
diff --git a/src/services/types.ts b/src/services/types.ts
index 5642cb45ea0ef5158c8c21fac7755239ffa3e0ff..2a059bd8888ed5cfa06fd754f21a48826d716477 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -120,6 +120,7 @@ export namespace CmdType {
   }
 
   export interface VergeConfig {
+    language?: string;
     theme_mode?: "light" | "dark";
     theme_blur?: boolean;
     traffic_graph?: boolean;
diff --git a/yarn.lock b/yarn.lock
index 6d242cc9ec12963314d99e3d85b99a5bb65ebd58..399e9f23c78a57412eff2d15904784c75f801a29 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -227,7 +227,7 @@
     "@babel/plugin-syntax-jsx" "^7.16.7"
     "@babel/types" "^7.17.0"
 
-"@babel/runtime@^7.13.10", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7":
+"@babel/runtime@^7.13.10", "@babel/runtime@^7.14.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7":
   version "7.17.2"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
   integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
@@ -1291,6 +1291,18 @@ hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
   dependencies:
     react-is "^16.7.0"
 
+html-escaper@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
+  integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
+
+html-parse-stringify@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
+  integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
+  dependencies:
+    void-elements "3.1.0"
+
 human-signals@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
@@ -1301,6 +1313,13 @@ husky@^7.0.0:
   resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535"
   integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==
 
+i18next@^21.6.14:
+  version "21.6.14"
+  resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.6.14.tgz#2bc199fba7f4da44b5952d7df0a3814a6e5c3943"
+  integrity sha512-XL6WyD+xlwQwbieXRlXhKWoLb/rkch50/rA+vl6untHnJ+aYnkQ0YDZciTWE78PPhOpbi2gR0LTJCJpiAhA+uQ==
+  dependencies:
+    "@babel/runtime" "^7.17.2"
+
 ignore@^5.1.4:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
@@ -1653,6 +1672,15 @@ react-dom@^17.0.2:
     object-assign "^4.1.1"
     scheduler "^0.20.2"
 
+react-i18next@^11.15.6:
+  version "11.15.6"
+  resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.15.6.tgz#693430fbee5ac7d0774bd88683575d62adb24afb"
+  integrity sha512-OUWcFdNgIA9swVx3JGIreuquglAinpRwB/HYrCprTN+s9BQDt9LSiY7x5DGc2JzVpwqtpoTV7oRUTOxEPNyUPw==
+  dependencies:
+    "@babel/runtime" "^7.14.5"
+    html-escaper "^2.0.2"
+    html-parse-stringify "^3.0.1"
+
 react-is@^16.13.1, react-is@^16.7.0:
   version "16.13.1"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -1901,6 +1929,11 @@ vite@^2.8.6:
   optionalDependencies:
     fsevents "~2.3.2"
 
+void-elements@3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
+  integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
+
 web-streams-polyfill@^3.0.3:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965"