mirror of
https://github.com/woodchen-ink/clash-and-dashboard.git
synced 2025-07-18 14:01:56 +08:00
Optimization: remove i18n to reduce bundle size
This commit is contained in:
parent
e72afde366
commit
f97b60f1cb
58
package-lock.json
generated
58
package-lock.json
generated
@ -952,6 +952,21 @@
|
||||
"integrity": "sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.135",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.135.tgz",
|
||||
"integrity": "sha512-Ed+tSZ9qM1oYpi5kzdsBuOzcAIn1wDW+e8TFJ50IMJMlSopGdJgKAbhHzN6h1E1OfjlGOr2JepzEWtg9NIfoNg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash-es": {
|
||||
"version": "4.17.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.3.tgz",
|
||||
"integrity": "sha512-iHI0i7ZAL1qepz1Y7f3EKg/zUMDwDfTzitx+AlHhJJvXwenP682ZyGbgPSc5Ej3eEAKVbNWKFuwOadCj5vBbYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
@ -5925,14 +5940,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"html-parse-stringify2": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz",
|
||||
"integrity": "sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=",
|
||||
"requires": {
|
||||
"void-elements": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"html-tags": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.0.0.tgz",
|
||||
@ -6095,19 +6102,6 @@
|
||||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
||||
"dev": true
|
||||
},
|
||||
"i18next": {
|
||||
"version": "17.0.6",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-17.0.6.tgz",
|
||||
"integrity": "sha512-bdNhzhcM6RG5m82RypVguCrAQNie/ycxW0Q5C6K9UDWD5hqApZfdJFbj4Ikz9jxIR+Ja1eg0yCQLhlCT+opwIg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1"
|
||||
}
|
||||
},
|
||||
"i18next-browser-languagedetector": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-3.0.1.tgz",
|
||||
"integrity": "sha512-WFjPLNPWl62uu07AHY2g+KsC9qz0tyMq+OZEB/H7N58YKL/JLiCz9U709gaR20Mule/Ppn+uyfVx5REJJjn1HA=="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
@ -7209,6 +7203,11 @@
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz",
|
||||
"integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q=="
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
@ -9256,15 +9255,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-i18next": {
|
||||
"version": "10.11.3",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-10.11.3.tgz",
|
||||
"integrity": "sha512-+kR0SQrTSws+NQfqK6Xe2FR6tMyfIPB6voxUqnLQ35Eh7T0vfe+v7eC4fF3pZlGGyn0qPKr294ACGbIz74u0sQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"html-parse-stringify2": "2.0.1"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.8.6",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
||||
@ -11799,7 +11789,8 @@
|
||||
"typescript": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz",
|
||||
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA=="
|
||||
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.4.10",
|
||||
@ -12260,11 +12251,6 @@
|
||||
"integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==",
|
||||
"dev": true
|
||||
},
|
||||
"void-elements": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
|
||||
"integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
|
||||
|
@ -34,6 +34,7 @@
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@hot-loader/react-dom": "^16.8.6",
|
||||
"@types/classnames": "^2.2.8",
|
||||
"@types/lodash-es": "^4.17.3",
|
||||
"@types/node": "^12.0.10",
|
||||
"@types/react": "^16.8.22",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
@ -64,6 +65,7 @@
|
||||
"tslint": "^5.18.0",
|
||||
"tslint-config-standard": "^8.0.1",
|
||||
"tslint-loader": "^3.6.0",
|
||||
"typescript": "^3.5.2",
|
||||
"webpack": "^4.35.2",
|
||||
"webpack-cli": "^3.3.5",
|
||||
"webpack-dev-middleware": "^3.7.0",
|
||||
@ -76,16 +78,13 @@
|
||||
"classnames": "^2.2.6",
|
||||
"dayjs": "^1.8.14",
|
||||
"eventemitter3": "^4.0.0",
|
||||
"i18next": "^17.0.6",
|
||||
"i18next-browser-languagedetector": "^3.0.1",
|
||||
"immer": "^3.1.3",
|
||||
"lodash-es": "^4.17.11",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-i18next": "^10.11.2",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.4",
|
||||
"typescript": "^3.5.2",
|
||||
"unstated-next": "^1.1.0",
|
||||
"use-immer": "^0.3.2",
|
||||
"yaml": "^1.6.0"
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React, { useState, useRef, useLayoutEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BaseComponentProps, I18nProps } from '@models'
|
||||
import { containers } from '@stores'
|
||||
import { BaseComponentProps } from '@models'
|
||||
import { noop } from '@lib/helper'
|
||||
import classnames from 'classnames'
|
||||
import './style.scss'
|
||||
|
||||
interface TagsProps extends BaseComponentProps, I18nProps {
|
||||
interface TagsProps extends BaseComponentProps {
|
||||
data: string[]
|
||||
onClick: (name: string) => void
|
||||
select: string
|
||||
@ -16,7 +16,8 @@ interface TagsProps extends BaseComponentProps, I18nProps {
|
||||
export function Tags (props: TagsProps) {
|
||||
const { className, data, onClick, select, canClick, rowHeight: rawHeight } = props
|
||||
|
||||
const { t } = useTranslation(['Proxies'])
|
||||
const { useTranslation } = containers.useI18n()
|
||||
const { t } = useTranslation('Proxies')
|
||||
const [expand, setExpand] = useState(false)
|
||||
const [showExtend, setShowExtend] = useState(false)
|
||||
|
||||
|
@ -2,7 +2,6 @@ import React, { useEffect } from 'react'
|
||||
import { Route, Redirect } from 'react-router-dom'
|
||||
import { hot } from 'react-hot-loader/root'
|
||||
import classnames from 'classnames'
|
||||
import { I18nProps } from '@models'
|
||||
import { isClashX } from '@lib/jsBridge'
|
||||
import './App.scss'
|
||||
|
||||
@ -15,9 +14,6 @@ import SlideBar from '@containers/Sidebar'
|
||||
import ExternalControllerModal from '@containers/ExternalControllerDrawer'
|
||||
import { getLogsStreamReader } from '@lib/request'
|
||||
|
||||
export interface AppProps extends I18nProps {
|
||||
}
|
||||
|
||||
function App () {
|
||||
useEffect(() => {
|
||||
getLogsStreamReader()
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useObject } from '@lib/hook'
|
||||
import { Modal, Input, Row, Col, Alert } from '@components'
|
||||
import { containers } from '@stores'
|
||||
import './style.scss'
|
||||
|
||||
export default function ExternalController () {
|
||||
const { t } = useTranslation(['Settings'])
|
||||
const { useTranslation } = containers.useI18n()
|
||||
const { t } = useTranslation('Settings')
|
||||
const { data: info, update, fetch } = containers.useAPIInfo()
|
||||
const { unauthorized: { hidden, visible } } = containers.useData()
|
||||
const { value, set, change } = useObject({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useLayoutEffect, useEffect, useRef, useState } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { containers } from '@stores'
|
||||
import { Card, Header } from '@components'
|
||||
import { getLogsStreamReader } from '@lib/request'
|
||||
import { StreamReader } from '@lib/streamer'
|
||||
@ -11,7 +11,8 @@ export default function Logs () {
|
||||
const listRef = useRef<HTMLUListElement>()
|
||||
const logsRef = useRef<Log[]>([])
|
||||
const [logs, setLogs] = useState<Log[]>([])
|
||||
const { t } = useTranslation(['Logs'])
|
||||
const { useTranslation } = containers.useI18n()
|
||||
const { t } = useTranslation('Logs')
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const ul = listRef.current
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useLayoutEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import EE from '@lib/event'
|
||||
import { Card, Header, Icon } from '@components'
|
||||
import { containers } from '@stores'
|
||||
@ -9,7 +8,8 @@ import './style.scss'
|
||||
|
||||
export default function Proxies () {
|
||||
const { data, fetch } = containers.useData()
|
||||
const { t } = useTranslation(['Proxies'])
|
||||
const { useTranslation } = containers.useI18n()
|
||||
const { t } = useTranslation('Proxies')
|
||||
|
||||
useLayoutEffect(() => {
|
||||
fetch()
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Header, Card, Row, Col } from '@components'
|
||||
import { containers } from '@stores'
|
||||
import { FixedSizeList as List } from 'react-window'
|
||||
@ -8,7 +7,8 @@ import './style.scss'
|
||||
|
||||
export default function Rules () {
|
||||
const { data, fetch } = containers.useData()
|
||||
const { t } = useTranslation(['Rules'])
|
||||
const { useTranslation } = containers.useI18n()
|
||||
const { t } = useTranslation('Rules')
|
||||
const { rules } = data
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -1,6 +1,4 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import i18next from 'i18next'
|
||||
import { Header, Card, Row, Col, Switch, ButtonSelect, ButtonSelectOptions, Input, Icon } from '@components'
|
||||
import { containers } from '@stores'
|
||||
import { updateConfig } from '@lib/request'
|
||||
@ -9,11 +7,7 @@ import { to } from '@lib/helper'
|
||||
import { isClashX, jsBridge } from '@lib/jsBridge'
|
||||
import './style.scss'
|
||||
|
||||
const languageOptions: ButtonSelectOptions[] = [{ label: '中文', value: 'zh' }, { label: 'English', value: 'en' }]
|
||||
|
||||
function changeLanguage (language: string) {
|
||||
i18next.changeLanguage(language)
|
||||
}
|
||||
const languageOptions: ButtonSelectOptions[] = [{ label: '中文', value: 'zh_CN' }, { label: 'English', value: 'en_US' }]
|
||||
|
||||
async function handleStartAtLoginChange (state: boolean) {
|
||||
await jsBridge.setStartAtLogin(state)
|
||||
@ -27,7 +21,8 @@ export default function Settings () {
|
||||
const { data: clashXData, fetch: fetchClashXData } = containers.useClashXData()
|
||||
const { data, fetch, unauthorized: { show } } = containers.useData()
|
||||
const { data: apiInfo } = containers.useAPIInfo()
|
||||
const { t, i18n } = useTranslation(['Settings'])
|
||||
const { useTranslation, setLang, lang } = containers.useI18n()
|
||||
const { t } = useTranslation('Settings')
|
||||
const { value: info, change } = useObject({
|
||||
socks5ProxyPort: 7891,
|
||||
httpProxyPort: 7890,
|
||||
@ -53,6 +48,10 @@ export default function Settings () {
|
||||
}
|
||||
}
|
||||
|
||||
function changeLanguage (language: string) {
|
||||
setLang(language)
|
||||
}
|
||||
|
||||
async function handleHttpPortSave () {
|
||||
const [, err] = await to(updateConfig({ 'port': info.httpProxyPort }))
|
||||
if (!err) {
|
||||
@ -111,7 +110,7 @@ export default function Settings () {
|
||||
<span className="label">{t('labels.language')}</span>
|
||||
</Col>
|
||||
<Col span={14} className="value-column">
|
||||
<ButtonSelect options={languageOptions} value={i18n.language.replace(/-.+$/, '')} onSelect={changeLanguage} />
|
||||
<ButtonSelect options={languageOptions} value={lang} onSelect={changeLanguage} />
|
||||
</Col>
|
||||
</Col>
|
||||
</Row>
|
||||
@ -199,7 +198,7 @@ export default function Settings () {
|
||||
<span className="check-icon">
|
||||
<Icon type="check" size={20} />
|
||||
</span>
|
||||
<p className="version-info">{t('versionString', { version: 'unknown' })}</p>
|
||||
<p className="version-info">{t('versionString')}</p>
|
||||
<span className="check-update-btn">{t('checkUpdate')}</span>
|
||||
</Card>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from 'react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import classnames from 'classnames'
|
||||
import { containers } from '@stores'
|
||||
|
||||
import './style.scss'
|
||||
const logo = require('@assets/logo.png')
|
||||
@ -17,7 +17,8 @@ interface SidebarProps {
|
||||
|
||||
export default function Sidebar (props: SidebarProps) {
|
||||
const { routes } = props
|
||||
const { t } = useTranslation(['SideBar'])
|
||||
const { useTranslation } = containers.useI18n()
|
||||
const { t } = useTranslation('SideBar')
|
||||
|
||||
const navlinks = routes.map(
|
||||
({ path, name, exact, noMobile }) => (
|
||||
|
@ -1,29 +1,70 @@
|
||||
import i18n from 'i18next'
|
||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||
import { useState, useCallback } from 'react'
|
||||
import get from 'lodash/get'
|
||||
import { getLocalStorageItem, setLocalStorageItem } from '@lib/helper'
|
||||
|
||||
// locales
|
||||
import en_US from './en_US'
|
||||
import zh_CN from './zh_CN'
|
||||
|
||||
const options = {
|
||||
fallbackLng: 'en_US',
|
||||
|
||||
ns: [
|
||||
'SideBar',
|
||||
'Settings',
|
||||
'Logs'
|
||||
],
|
||||
|
||||
resources: {
|
||||
en: en_US,
|
||||
zh: zh_CN
|
||||
},
|
||||
|
||||
react: {
|
||||
wait: true
|
||||
}
|
||||
const Language = {
|
||||
en_US,
|
||||
zh_CN
|
||||
}
|
||||
|
||||
i18n.use(LanguageDetector).init(options)
|
||||
const languageKey = 'language'
|
||||
|
||||
export default i18n
|
||||
const locales = Object.keys(Language)
|
||||
|
||||
function getNavigatorLanguage (): string[] {
|
||||
const found: string[] = []
|
||||
|
||||
if (window.navigator) {
|
||||
if (window.navigator.languages) {
|
||||
for (const lan of window.navigator.languages) {
|
||||
found.push(lan)
|
||||
}
|
||||
} else if (window.navigator.language) {
|
||||
found.push(navigator.language)
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
function getLanguage () {
|
||||
const localLanguage = getLocalStorageItem(languageKey)
|
||||
if (localLanguage && locales.includes(localLanguage)) {
|
||||
return localLanguage
|
||||
}
|
||||
|
||||
const navigatorLanguage = getNavigatorLanguage()
|
||||
for (const language of navigatorLanguage) {
|
||||
if (language.includes('zh')) {
|
||||
return 'zh_CN'
|
||||
} else if (language.includes('us')) {
|
||||
return 'en_US'
|
||||
}
|
||||
}
|
||||
|
||||
return 'en_US'
|
||||
}
|
||||
|
||||
export function useI18n () {
|
||||
const [lang, set] = useState(getLanguage())
|
||||
|
||||
function setLang (lang: string) {
|
||||
set(lang)
|
||||
setLocalStorageItem(languageKey, lang)
|
||||
}
|
||||
|
||||
const useTranslation = useCallback(
|
||||
function (namespace: string) {
|
||||
function t (path: string) {
|
||||
return get(Language[lang][namespace], path) as string
|
||||
}
|
||||
return { t }
|
||||
},
|
||||
[lang]
|
||||
)
|
||||
|
||||
return { lang, locales, setLang, useTranslation }
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
export interface I18nProps {
|
||||
t? (
|
||||
key: string,
|
||||
variables?: {
|
||||
[key: string]: any
|
||||
}
|
||||
): string
|
||||
|
||||
lng?: string
|
||||
}
|
@ -2,5 +2,4 @@ export * from './BaseProps'
|
||||
export * from './Config'
|
||||
export * from './Proxy'
|
||||
export * from './Rule'
|
||||
export * from './I18n'
|
||||
export * from './Cipher'
|
||||
|
@ -1,19 +1,15 @@
|
||||
import * as React from 'react'
|
||||
import { render } from 'react-dom'
|
||||
import { HashRouter } from 'react-router-dom'
|
||||
import { I18nextProvider } from 'react-i18next'
|
||||
import { Provider as Global } from '@stores'
|
||||
import App from '@containers/App'
|
||||
import i18n from '@i18n'
|
||||
|
||||
export default function renderApp () {
|
||||
const rootEl = document.getElementById('root')
|
||||
const AppInstance = (
|
||||
<Global>
|
||||
<HashRouter>
|
||||
<I18nextProvider i18n={ i18n }>
|
||||
<App />
|
||||
</I18nextProvider>
|
||||
<App />
|
||||
</HashRouter>
|
||||
</Global>
|
||||
)
|
||||
|
@ -4,6 +4,7 @@ import * as API from '@lib/request'
|
||||
import { useObject, composeContainer } from '@lib/hook'
|
||||
import { jsBridge } from '@lib/jsBridge'
|
||||
import { setLocalStorageItem, partition, to } from '@lib/helper'
|
||||
import { useI18n } from '@i18n'
|
||||
|
||||
function useData () {
|
||||
const { value: data, change } = useObject<Models.Data>({
|
||||
@ -98,7 +99,8 @@ function useClashXData () {
|
||||
const { Provider, containers } = composeContainer({
|
||||
useData,
|
||||
useAPIInfo,
|
||||
useClashXData
|
||||
useClashXData,
|
||||
useI18n
|
||||
})
|
||||
|
||||
export { Provider, containers }
|
||||
|
Loading…
x
Reference in New Issue
Block a user