summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHaishan <[email protected]>2020-12-06 14:57:59 +0800
committerHaishan <[email protected]>2020-12-06 20:19:51 +0800
commit8a50ef4ef2f6f6044d36ea2f4fe06e663083972e (patch)
treeda098c1434b5f745f391330dde37b6468deec45b /src
parenta8c6cd23ce2b585362f515080b2167990c554fed (diff)
feat: initial Chinese UI language support
Diffstat (limited to 'src')
-rw-r--r--src/app.tsx1
-rw-r--r--src/components/Config.module.css6
-rw-r--r--src/components/Config.tsx51
-rw-r--r--src/components/Connections.tsx10
-rw-r--r--src/components/Home.tsx4
-rw-r--r--src/components/Logs.tsx8
-rw-r--r--src/components/Rules.tsx5
-rw-r--r--src/components/SideBar.module.css1
-rw-r--r--src/components/SideBar.tsx8
-rw-r--r--src/components/TrafficChart.tsx12
-rw-r--r--src/components/TrafficNow.tsx14
-rw-r--r--src/components/proxies/Proxies.tsx9
-rw-r--r--src/components/proxies/Settings.tsx24
-rw-r--r--src/components/shared/Select.module.css1
-rw-r--r--src/custom.d.ts4
-rw-r--r--src/i18n/en.ts34
-rw-r--r--src/i18n/zh.ts34
-rw-r--r--src/misc/i18n.ts61
18 files changed, 238 insertions, 49 deletions
diff --git a/src/app.tsx b/src/app.tsx
index b4cc818..5d2f226 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -1,4 +1,5 @@
import 'modern-normalize/modern-normalize.css';
+import './misc/i18n';
import React from 'react';
import ReactDOM from 'react-dom';
diff --git a/src/components/Config.module.css b/src/components/Config.module.css
index d7fa40a..1f71765 100644
--- a/src/components/Config.module.css
+++ b/src/components/Config.module.css
@@ -11,7 +11,7 @@
.section {
padding: 6px 15px 15px;
@media (--breakpoint-not-small) {
- padding: 10px 40px 40px;
+ padding: 0 40px 40px;
}
}
@@ -28,3 +28,7 @@
.label {
padding: 16px 0;
}
+
+.narrow {
+ width: 360px;
+}
diff --git a/src/components/Config.tsx b/src/components/Config.tsx
index 981eae1..4659e16 100644
--- a/src/components/Config.tsx
+++ b/src/components/Config.tsx
@@ -1,5 +1,8 @@
-import PropTypes from 'prop-types';
-import React from 'react';
+import * as React from 'react';
+import { useTranslation } from 'react-i18next';
+import Select from 'src/components/shared/Select';
+import { ClashGeneralConfig, DispatchFn, State } from 'src/store/types';
+import { ClashAPIConfig } from 'src/types';
import {
getClashAPIConfig,
@@ -67,12 +70,17 @@ const portFields = [
{ key: 'redir-port', label: 'Redir Port' },
];
-const mapState = (s) => ({
+const langOptions = [
+ ['zh', '中文'],
+ ['en', 'English'],
+];
+
+const mapState = (s: State) => ({
configs: getConfigs(s),
apiConfig: getClashAPIConfig(s),
});
-const mapState2 = (s) => ({
+const mapState2 = (s: State) => ({
selectedChartStyleIndex: getSelectedChartStyleIndex(s),
latencyTestUrl: getLatencyTestUrl(s),
apiConfig: getClashAPIConfig(s),
@@ -88,13 +96,21 @@ function ConfigContainer({ dispatch, configs, apiConfig }) {
return <Config configs={configs} />;
}
+type ConfigImplProps = {
+ dispatch: DispatchFn;
+ configs: ClashGeneralConfig;
+ selectedChartStyleIndex: number;
+ latencyTestUrl: string;
+ apiConfig: ClashAPIConfig;
+};
+
function ConfigImpl({
dispatch,
configs,
selectedChartStyleIndex,
latencyTestUrl,
apiConfig,
-}) {
+}: ConfigImplProps) {
const [configState, setConfigStateInternal] = useState(configs);
const refConfigs = useRef(configs);
useEffect(() => {
@@ -188,9 +204,11 @@ function ConfigImpl({
return typeof m === 'string' && m[0].toUpperCase() + m.slice(1);
}, [configState.mode]);
+ const { t, i18n } = useTranslation();
+
return (
<div>
- <ContentHeader title="Config" />
+ <ContentHeader title={t('Config')} />
<div className={s0.root}>
{portFields.map((f) =>
configState[f.key] !== undefined ? (
@@ -242,7 +260,7 @@ function ConfigImpl({
<div className={s0.section}>
<div>
- <div className={s0.label}>Chart Style</div>
+ <div className={s0.label}>{t('chart_style')}</div>
<Selection2
OptionComponent={TrafficChartSample}
optionPropsList={propsList}
@@ -250,8 +268,8 @@ function ConfigImpl({
onChange={selectChartStyleIndex}
/>
</div>
- <div style={{ maxWidth: 360 }}>
- <div className={s0.label}>Latency Test URL</div>
+ <div className={s0.narrow}>
+ <div className={s0.label}>{t('latency_test_url')}</div>
<SelfControlledInput
name="latencyTestUrl"
type="text"
@@ -263,12 +281,17 @@ function ConfigImpl({
<div className={s0.label}>Action</div>
<Button label="Switch backend" onClick={openAPIConfigModal} />
</div>
+ <div>
+ <div className={s0.label}>{t('lang')}</div>
+ <div className={s0.narrow}>
+ <Select
+ options={langOptions}
+ selected={i18n.language}
+ onChange={(e) => i18n.changeLanguage(e.target.value)}
+ />
+ </div>
+ </div>
</div>
</div>
);
}
-
-// @ts-expect-error ts-migrate(2339) FIXME: Property 'propTypes' does not exist on type '(prop... Remove this comment to see the full error message
-Config.propTypes = {
- configs: PropTypes.object,
-};
diff --git a/src/components/Connections.tsx b/src/components/Connections.tsx
index 078d32e..63b0010 100644
--- a/src/components/Connections.tsx
+++ b/src/components/Connections.tsx
@@ -2,6 +2,7 @@ import './Connections.css';
import React from 'react';
import { Pause, Play, X as IconClose } from 'react-feather';
+import { useTranslation } from 'react-i18next';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import { ConnectionItem } from 'src/api/connections';
import { State } from 'src/store/types';
@@ -176,9 +177,12 @@ function Conn({ apiConfig }) {
useEffect(() => {
return connAPI.fetchData(apiConfig, read);
}, [apiConfig, read]);
+
+ const { t } = useTranslation();
+
return (
<div>
- <ContentHeader title="Connections" />
+ <ContentHeader title={t('Connections')} />
<Tabs>
<div
style={{
@@ -189,14 +193,14 @@ function Conn({ apiConfig }) {
>
<TabList>
<Tab>
- <span>Active</span>
+ <span>{t('Active')}</span>
<span className={s.connQty}>
{/* @ts-expect-error ts-migrate(2786) FIXME: 'ConnQty' cannot be used as a JSX component. */}
<ConnQty qty={filteredConns.length} />
</span>
</Tab>
<Tab>
- <span>Closed</span>
+ <span>{t('Closed')}</span>
<span className={s.connQty}>
{/* @ts-expect-error ts-migrate(2786) FIXME: 'ConnQty' cannot be used as a JSX component. */}
<ConnQty qty={filteredClosedConns.length} />
diff --git a/src/components/Home.tsx b/src/components/Home.tsx
index 532379b..a6df373 100644
--- a/src/components/Home.tsx
+++ b/src/components/Home.tsx
@@ -1,4 +1,5 @@
import React, { Suspense } from 'react';
+import { useTranslation } from 'react-i18next';
import ContentHeader from './ContentHeader';
import s0 from './Home.module.css';
@@ -7,9 +8,10 @@ import TrafficChart from './TrafficChart';
import TrafficNow from './TrafficNow';
export default function Home() {
+ const { t } = useTranslation();
return (
<div>
- <ContentHeader title="Overview" />
+ <ContentHeader title={t('Overview')} />
<div className={s0.root}>
<div>
<TrafficNow />
diff --git a/src/components/Logs.tsx b/src/components/Logs.tsx
index 5bc1f5d..3a4dabd 100644
--- a/src/components/Logs.tsx
+++ b/src/components/Logs.tsx
@@ -1,5 +1,6 @@
import cx from 'clsx';
-import React from 'react';
+import * as React from 'react';
+import { useTranslation } from 'react-i18next';
import { areEqual, FixedSizeList as List } from 'react-window';
import { fetchLogs } from '../api/logs';
@@ -73,10 +74,11 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) {
fetchLogs({ ...apiConfig, logLevel }, appendLogInternal);
}, [apiConfig, logLevel, appendLogInternal]);
const [refLogsContainer, containerHeight] = useRemainingViewPortHeight();
+ const { t } = useTranslation();
return (
<div>
- <ContentHeader title="Logs" />
+ <ContentHeader title={t('Logs')} />
<LogSearch />
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'number | MutableRefObject<any>' is not assig... Remove this comment to see the full error message */}
<div ref={refLogsContainer} style={{ paddingBottom }}>
@@ -89,7 +91,7 @@ function Logs({ dispatch, logLevel, apiConfig, logs }) {
<div className={s0.logPlaceholderIcon}>
<SvgYacd width={200} height={200} />
</div>
- <div>No logs yet, hang tight...</div>
+ <div>{t('no_logs')}</div>
</div>
) : (
<div className={s0.logsWrapper}>
diff --git a/src/components/Rules.tsx b/src/components/Rules.tsx
index dab479c..008ce3c 100644
--- a/src/components/Rules.tsx
+++ b/src/components/Rules.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import { RotateCw } from 'react-feather';
+import { useTranslation } from 'react-i18next';
import { queryCache, useQuery } from 'react-query';
import { areEqual, VariableSizeList } from 'react-window';
import { useRecoilState } from 'recoil';
@@ -114,10 +115,12 @@ function Rules({ apiConfig }) {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ rules: RuleItem[]; provider: {... Remove this comment to see the full error message
const getItemSize = getItemSizeFactory({ rules, provider });
+ const { t } = useTranslation();
+
return (
<div>
<div className={s.header}>
- <ContentHeader title="Rules" />
+ <ContentHeader title={t('Rules')} />
<TextFilter />
</div>
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'number | MutableRefObject<any>' is not assig... Remove this comment to see the full error message */}
diff --git a/src/components/SideBar.module.css b/src/components/SideBar.module.css
index 7ecb3c3..744d29d 100644
--- a/src/components/SideBar.module.css
+++ b/src/components/SideBar.module.css
@@ -1,5 +1,6 @@
.root {
background: var(--color-bg-sidebar);
+ min-width: 150px;
position: relative;
}
diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx
index 6cfd829..973f003 100644
--- a/src/components/SideBar.tsx
+++ b/src/components/SideBar.tsx
@@ -2,6 +2,7 @@ import Tooltip from '@reach/tooltip';
import cx from 'clsx';
import * as React from 'react';
import { Info } from 'react-feather';
+import { useTranslation } from 'react-i18next';
import {
FcAreaChart,
FcDocument,
@@ -85,6 +86,7 @@ const pages = [
];
function SideBar({ dispatch, theme }) {
+ const { t } = useTranslation();
const location = useLocation();
const switchThemeHooked = useCallback(() => {
dispatch(switchTheme());
@@ -99,13 +101,13 @@ function SideBar({ dispatch, theme }) {
to={to}
isActive={location.pathname === to}
iconId={iconId}
- labelText={labelText}
+ labelText={t(labelText)}
/>
))}
</div>
<div className={s.footer}>
<Tooltip
- label="theme"
+ label={t('theme')}
aria-label={
'switch to ' + (theme === 'light' ? 'dark' : 'light') + ' theme'
}
@@ -117,7 +119,7 @@ function SideBar({ dispatch, theme }) {
{theme === 'light' ? <MoonA /> : <Sun />}
</button>
</Tooltip>
- <Tooltip label="about">
+ <Tooltip label={t('about')}>
<Link to="/about" className={s.iconWrapper}>
<Info size={20} />
</Link>
diff --git a/src/components/TrafficChart.tsx b/src/components/TrafficChart.tsx
index 5fcdf7d..056cac6 100644
--- a/src/components/TrafficChart.tsx
+++ b/src/components/TrafficChart.tsx
@@ -1,4 +1,5 @@
-import React, { useMemo } from 'react';
+import * as React from 'react';
+import { useTranslation } from 'react-i18next';
import { fetchData } from '../api/traffic';
import useLineChart from '../hooks/useLineChart';
@@ -10,6 +11,8 @@ import {
import { getClashAPIConfig, getSelectedChartStyleIndex } from '../store/app';
import { connect } from './StateProvider';
+const { useMemo } = React;
+
const chartWrapperStyle = {
// make chartjs chart responsive
position: 'relative',
@@ -26,6 +29,7 @@ export default connect(mapState)(TrafficChart);
function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
const Chart = chartJSResource.read();
const traffic = fetchData(apiConfig);
+ const { t } = useTranslation();
const data = useMemo(
() => ({
labels: traffic.labels,
@@ -33,18 +37,18 @@ function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
{
...commonDataSetProps,
...chartStyles[selectedChartStyleIndex].up,
- label: 'Up',
+ label: t('Up'),
data: traffic.up,
},
{
...commonDataSetProps,
...chartStyles[selectedChartStyleIndex].down,
- label: 'Down',
+ label: t('Down'),
data: traffic.down,
},
],
}),
- [traffic, selectedChartStyleIndex]
+ [traffic, selectedChartStyleIndex, t]
);
useLineChart(Chart, 'trafficChart', data, traffic);
diff --git a/src/components/TrafficNow.tsx b/src/components/TrafficNow.tsx
index cfab65b..fbcc4e9 100644
--- a/src/components/TrafficNow.tsx
+++ b/src/components/TrafficNow.tsx
@@ -1,4 +1,5 @@
-import React from 'react';
+import * as React from 'react';
+import { useTranslation } from 'react-i18next';
import * as connAPI from '../api/connections';
import { fetchData } from '../api/traffic';
@@ -15,28 +16,29 @@ const mapState = (s) => ({
export default connect(mapState)(TrafficNow);
function TrafficNow({ apiConfig }) {
+ const { t } = useTranslation();
const { upStr, downStr } = useSpeed(apiConfig);
const { upTotal, dlTotal, connNumber } = useConnection(apiConfig);
return (
<div className={s0.TrafficNow}>
<div className="sec">
- <div>Upload</div>
+ <div>{t('Upload')}</div>
<div>{upStr}</div>
</div>
<div className="sec">
- <div>Download</div>
+ <div>{t('Download')}</div>
<div>{downStr}</div>
</div>
<div className="sec">
- <div>Upload Total</div>
+ <div>{t('Upload Total')}</div>
<div>{upTotal}</div>
</div>
<div className="sec">
- <div>Download Total</div>
+ <div>{t('Download Total')}</div>
<div>{dlTotal}</div>
</div>
<div className="sec">
- <div>Active Connections</div>
+ <div>{t('Active Connections')}</div>
<div>{connNumber}</div>
</div>
</div>
diff --git a/src/components/proxies/Proxies.tsx b/src/components/proxies/Proxies.tsx
index 7fbe99c..6c3db7d 100644
--- a/src/components/proxies/Proxies.tsx
+++ b/src/components/proxies/Proxies.tsx
@@ -1,6 +1,7 @@
import Tooltip from '@reach/tooltip';
import * as React from 'react';
import { Zap } from 'react-feather';
+import { useTranslation } from 'react-i18next';
import { getClashAPIConfig } from '../../store/app';
import {
@@ -80,6 +81,8 @@ function Proxies({
proxies: { closeModalClosePrevConns, closePrevConnsAndTheModal },
} = useStoreActions();
+ const { t } = useTranslation();
+
return (
<>
<BaseModal
@@ -89,12 +92,12 @@ function Proxies({
<Settings />
</BaseModal>
<div className={s0.topBar}>
- <ContentHeader title="Proxies" />
+ <ContentHeader title={t('Proxies')} />
<div className={s0.topBarRight}>
<div className={s0.textFilterContainer}>
<TextFilter />
</div>
- <Tooltip label="settings">
+ <Tooltip label={t('settings')}>
<Button kind="minimal" onClick={() => setIsSettingsModalOpen(true)}>
<Equalizer size={16} />
</Button>
@@ -120,7 +123,7 @@ function Proxies({
<Fab
icon={isTestingLatency ? <ColorZap /> : <Zap width={16} height={16} />}
onClick={requestDelayAllFn}
- text="Test Latency"
+ text={t('Test Latency')}
position={fabPosition}
/>
<BaseModal
diff --git a/src/components/proxies/Settings.tsx b/src/components/proxies/Settings.tsx
index bb859ac..703fb0d 100644
--- a/src/components/proxies/Settings.tsx
+++ b/src/components/proxies/Settings.tsx
@@ -1,21 +1,22 @@
import * as React from 'react';
+import { useTranslation } from 'react-i18next';
+import Select from 'src/components/shared/Select';
import {
getAutoCloseOldConns,
getHideUnavailableProxies,
getProxySortBy,
} from '../../store/app';
-import Select from '../shared/Select';
import { connect, useStoreActions } from '../StateProvider';
import Switch from '../SwitchThemed';
import s from './Settings.module.css';
const options = [
- ['Natural', 'Original order in config file'],
- ['LatencyAsc', 'By latency from small to big'],
- ['LatencyDesc', 'By latency from big to small'],
- ['NameAsc', 'By name alphabetically (A-Z)'],
- ['NameDesc', 'By name alphabetically (Z-A)'],
+ ['Natural', 'order_natural'],
+ ['LatencyAsc', 'order_latency_asc'],
+ ['LatencyDesc', 'order_latency_desc'],
+ ['NameAsc', 'order_name_asc'],
+ ['NameDesc', 'order_name_desc'],
];
const { useCallback } = React;
@@ -38,13 +39,16 @@ function Settings({ appConfig }) {
},
[updateAppConfig]
);
+ const { t } = useTranslation();
return (
<>
<div className={s.labeledInput}>
- <span>Sorting in group</span>
+ <span>{t('sort_in_grp')}</span>
<div>
<Select
- options={options}
+ options={options.map((o) => {
+ return [o[0], t(o[1])];
+ })}
selected={appConfig.proxySortBy}
onChange={handleProxySortByOnChange}
/>
@@ -52,7 +56,7 @@ function Settings({ appConfig }) {
</div>
<hr />
<div className={s.labeledInput}>
- <span>Hide unavailable proxies</span>
+ <span>{t('hide_unavail_proxies')}</span>
<div>
<Switch
name="hideUnavailableProxies"
@@ -62,7 +66,7 @@ function Settings({ appConfig }) {
</div>
</div>
<div className={s.labeledInput}>
- <span>Automatically close old connections</span>
+ <span>{t('auto_close_conns')}</span>
<div>
<Switch
name="autoCloseOldConns"
diff --git a/src/components/shared/Select.module.css b/src/components/shared/Select.module.css
index cbd1ffe..3ea430e 100644
--- a/src/components/shared/Select.module.css
+++ b/src/components/shared/Select.module.css
@@ -1,5 +1,6 @@
.select {
height: 30px;
+ line-height: 1.5;
width: 100%;
padding-left: 8px;
appearance: none;
diff --git a/src/custom.d.ts b/src/custom.d.ts
index db7360a..3a9b7ac 100644
--- a/src/custom.d.ts
+++ b/src/custom.d.ts
@@ -7,6 +7,10 @@ declare module '*.module.css' {
export default classes;
}
+interface Window {
+ i18n: any;
+}
+
// webpack definePlugin replacing variables
declare const __VERSION__: string;
declare const __DEV__: string;
diff --git a/src/i18n/en.ts b/src/i18n/en.ts
new file mode 100644
index 0000000..be720c3
--- /dev/null
+++ b/src/i18n/en.ts
@@ -0,0 +1,34 @@
+export const data = {
+ Overview: 'Overview',
+ Proxies: 'Proxies',
+ Rules: 'Rules',
+ Conns: 'Conns',
+ Config: 'Config',
+ Logs: 'Logs',
+ Upload: 'Upload',
+ Download: 'Download',
+ 'Upload Total': 'Upload Total',
+ 'Download Total': 'Download Total',
+ 'Active Connections': 'Active Connections',
+ Up: 'Up',
+ Down: 'Down',
+ 'Test Latency': 'Test Latency',
+ settings: 'settings',
+ sort_in_grp: 'Sorting in group',
+ hide_unavail_proxies: 'Hide unavailable proxies',
+ auto_close_conns: 'Automatically close old connections',
+ order_natural: 'Original order in config file',
+ order_latency_asc: 'By latency from small to big',
+ order_latency_desc: 'By latency from big to small',
+ order_name_asc: 'By name alphabetically (A-Z)',
+ order_name_desc: 'By name alphabetically (Z-A)',
+ Connections: 'Connections',
+ Active: 'Active',
+ Closed: 'Closed',
+ theme: 'theme',
+ about: 'about',
+ no_logs: 'No logs yet, hang tight...',
+ chart_style: 'Chart Style',
+ latency_test_url: 'Latency Test URL',
+ lang: 'Language',
+};
diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts
new file mode 100644
index 0000000..0c28260
--- /dev/null
+++ b/src/i18n/zh.ts
@@ -0,0 +1,34 @@
+export const data = {
+ Overview: '概述',
+ Proxies: '代理',
+ Rules: '规则',
+ Conns: '连接',
+ Config: '配置',
+ Logs: '日志',
+ Upload: '上传',
+ Download: '下载',
+ 'Upload Total': '上传总量',
+ 'Download Total': '下载总量',
+ 'Active Connections': '活动连接',
+ Up: '上传',
+ Down: '下载',
+ 'Test Latency': '延迟测速',
+ settings: '设置',
+ sort_in_grp: '代理组条目排序',
+ hide_unavail_proxies: '隐藏不可用代理',
+ auto_close_conns: '切换代理时自动断开旧连接',
+ order_natural: '原 config 文件中的排序',
+ order_latency_asc: '按延迟从小到大',
+ order_latency_desc: '按延迟从大到小',
+ order_name_asc: '按名称字母排序 (A-Z)',
+ order_name_desc: '按名称字母排序 (Z-A)',
+ Connections: '连接',
+ Active: '活动',
+ Closed: '已断开',
+ theme: '主题',
+ about: '关于',
+ no_logs: '暂无日志...',
+ chart_style: '流量图样式',
+ latency_test_url: '延迟测速 URL',
+ lang: '语言',
+};
diff --git a/src/misc/i18n.ts b/src/misc/i18n.ts
new file mode 100644
index 0000000..023c5d7
--- /dev/null
+++ b/src/misc/i18n.ts
@@ -0,0 +1,61 @@
+import i18next from 'i18next';
+import LanguageDetector from 'i18next-browser-languagedetector';
+import HttpBackend from 'i18next-http-backend';
+import { initReactI18next } from 'react-i18next';
+
+const allLocales = {
+ zh: import('src/i18n/zh'),
+ en: import('src/i18n/en'),
+};
+
+type BackendRequestCallback = (
+ err: null,
+ result: { status: number; data: any }
+) => void;
+
+i18next
+ .use(HttpBackend)
+ .use(initReactI18next)
+ .use(LanguageDetector)
+ .init({
+ debug: process.env.NODE_ENV === 'development',
+ // resources,
+ backend: {
+ loadPath: '/__{{lng}}/{{ns}}.json',
+ request: function (
+ _options: any,
+ url: string,
+ _payload: any,
+ callback: BackendRequestCallback
+ ) {
+ let p: PromiseLike<{ data: any }>;
+
+ switch (url) {
+ case '/__zh/translation.json':
+ p = allLocales.zh;
+ break;
+ case '/__en/translation.json':
+ default:
+ p = allLocales.en;
+ break;
+ }
+
+ if (p) {
+ p.then((mod) => {
+ callback(null, { status: 200, data: mod.data });
+ });
+ }
+ },
+ },
+ supportedLngs: ['en', 'zh'],
+ fallbackLng: 'en',
+ interpolation: {
+ escapeValue: false,
+ },
+ });
+
+if (process.env.NODE_ENV === 'development') {
+ window.i18n = i18next;
+}
+
+export default i18next;