diff --git a/client/app/i18n/index.js b/client/app/i18n/index.js
new file mode 100644
index 0000000000..d285c50ed2
--- /dev/null
+++ b/client/app/i18n/index.js
@@ -0,0 +1,22 @@
+import i18n from "i18next";
+import LanguageDetector from "i18next-browser-languagedetector";
+import { initReactI18next } from "react-i18next";
+import en from "./locales/en.json";
+import zh from "./locales/zh.json";
+
+i18n
+ .use(initReactI18next)
+ .use(LanguageDetector)
+ .init({
+ resources: {
+ en: { translation: en },
+ zh: { translation: zh },
+ },
+ fallbackLng: "zh",
+ preload: ["en", "zh"],
+ interpolation: {
+ escapeValue: false,
+ },
+ });
+
+export default i18n;
diff --git a/client/app/i18n/locales/en.json b/client/app/i18n/locales/en.json
new file mode 100644
index 0000000000..3c49bd5429
--- /dev/null
+++ b/client/app/i18n/locales/en.json
@@ -0,0 +1,3 @@
+{
+ "dashboard": "dashboard"
+}
diff --git a/client/app/i18n/locales/zh.json b/client/app/i18n/locales/zh.json
new file mode 100644
index 0000000000..f947fd472d
--- /dev/null
+++ b/client/app/i18n/locales/zh.json
@@ -0,0 +1,4 @@
+{
+ "dashboard": "看板",
+ "Favorite Dashboards": "收藏的看板"
+}
diff --git a/client/app/index.js b/client/app/index.js
index 9cafdb1f4b..32e8a118f9 100644
--- a/client/app/index.js
+++ b/client/app/index.js
@@ -5,6 +5,7 @@ import "@/config";
import ApplicationArea from "@/components/ApplicationArea";
import offlineListener from "@/services/offline-listener";
+import "./i18n";
ReactDOM.render(, document.getElementById("application-root"), () => {
offlineListener.init();
diff --git a/client/app/pages/home/components/FavoritesList.jsx b/client/app/pages/home/components/FavoritesList.jsx
index 118930d679..54146ff183 100644
--- a/client/app/pages/home/components/FavoritesList.jsx
+++ b/client/app/pages/home/components/FavoritesList.jsx
@@ -7,6 +7,7 @@ import LoadingOutlinedIcon from "@ant-design/icons/LoadingOutlined";
import { Dashboard } from "@/services/dashboard";
import { Query } from "@/services/query";
+import { useTranslation } from "react-i18next";
export function FavoriteList({ title, resource, itemUrl, emptyState }) {
const [items, setItems] = useState([]);
@@ -53,13 +54,15 @@ FavoriteList.propTypes = {
FavoriteList.defaultProps = { emptyState: null };
export function DashboardAndQueryFavoritesList() {
+ const { t } = useTranslation();
+
return (
dashboard.url}
emptyState={
diff --git a/package.json b/package.json
index e84e4ef44f..233624a26e 100644
--- a/package.json
+++ b/package.json
@@ -58,6 +58,8 @@
"font-awesome": "^4.7.0",
"history": "^4.10.1",
"hoist-non-react-statics": "^3.3.0",
+ "i18next": "^22.4.15",
+ "i18next-browser-languagedetector": "^7.0.1",
"markdown": "0.5.0",
"material-design-iconic-font": "^2.2.0",
"moment": "^2.19.3",
@@ -71,6 +73,7 @@
"react-ace": "^9.1.1",
"react-dom": "^16.14.0",
"react-grid-layout": "^0.18.2",
+ "react-i18next": "^12.2.2",
"react-resizable": "^1.10.1",
"react-virtualized": "^9.21.2",
"sql-formatter": "git+https://github.com/getredash/sql-formatter.git",
diff --git a/yarn.lock b/yarn.lock
index 507aa2fc04..c084199e9e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8129,6 +8129,20 @@ hyperlinker@^1.0.0:
resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e"
integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==
+i18next-browser-languagedetector@^7.0.1:
+ version "7.0.2"
+ resolved "https://registry.npmmirror.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.2.tgz#22d8ed48411750c1a1828ac2031c816a5108e55f"
+ integrity sha512-5ViaK+gikxfqZ9M3jJ7gJkUzzu/p3HwiqfLoL1bdiL7CUb0IylcTyVLdPaTU3pH5VFWFCiGFuJDg3VkLUikWgg==
+ dependencies:
+ "@babel/runtime" "^7.19.4"
+
+i18next@^22.4.15:
+ version "22.5.1"
+ resolved "https://registry.npmmirror.com/i18next/-/i18next-22.5.1.tgz#99df0b318741a506000c243429a7352e5f44d424"
+ integrity sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==
+ dependencies:
+ "@babel/runtime" "^7.20.6"
+
iconv-lite@0.4.23:
version "0.4.23"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
@@ -13060,6 +13074,14 @@ react-grid-layout@^0.18.2:
react-draggable "^4.0.0"
react-resizable "^1.9.0"
+react-i18next@^12.2.2:
+ version "12.3.1"
+ resolved "https://registry.npmmirror.com/react-i18next/-/react-i18next-12.3.1.tgz#30134a41a2a71c61dc69c6383504929aed1c99e7"
+ integrity sha512-5v8E2XjZDFzK7K87eSwC7AJcAkcLt5xYZ4+yTPDAW1i7C93oOY1dnr4BaQM7un4Hm+GmghuiPvevWwlca5PwDA==
+ dependencies:
+ "@babel/runtime" "^7.20.6"
+ html-parse-stringify "^3.0.1"
+
react-is@^16.12.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"