From eebd471a68c8a4faf4db28d8bd258b107144c36e Mon Sep 17 00:00:00 2001
From: Kaz <54239670+object-kaz@users.noreply.github.com>
Date: Sun, 19 Jun 2022 00:08:12 +0800
Subject: [PATCH] feat: language detect and save (#139)
---
packages/web/.vscode/settings.json | 11 ++++++--
packages/web/auto-imports.d.ts | 26 ++++++++++++-------
packages/web/package.json | 7 +++--
packages/web/src/App.vue | 6 +++++
.../src/layout/components/LanguageMenu.vue | 8 +++---
packages/web/src/modules/locales.ts | 23 ++++++++++++++++
packages/web/src/store/config.ts | 13 ++++++++++
packages/web/src/store/index.ts | 1 +
8 files changed, 79 insertions(+), 16 deletions(-)
create mode 100644 packages/web/src/store/config.ts
diff --git a/packages/web/.vscode/settings.json b/packages/web/.vscode/settings.json
index 0909de3172f..81367cc44e2 100644
--- a/packages/web/.vscode/settings.json
+++ b/packages/web/.vscode/settings.json
@@ -7,7 +7,7 @@
"files.associations": {
"*.css": "postcss"
},
- "i18n-ally.sourceLanguage": "en",
+ "i18n-ally.sourceLanguage": "zh-CN",
"i18n-ally.keystyle": "nested",
"i18n-ally.localesPaths": "locales",
"i18n-ally.sortKeys": true,
@@ -16,5 +16,12 @@
"vue-sfc",
"i18next"
],
- "i18n-ally.extract.autoDetect": true
+ "i18n-ally.extract.autoDetect": true,
+ "i18n-ally.refactor.templates": [{
+ "name": "i18n-ally-vue-sfc",
+ "extensions": [
+ "ts",
+ "i18n.global.t('${key}')"
+ ]
+ }]
}
\ No newline at end of file
diff --git a/packages/web/auto-imports.d.ts b/packages/web/auto-imports.d.ts
index 05fbeaca3f9..5b0c2c317ba 100644
--- a/packages/web/auto-imports.d.ts
+++ b/packages/web/auto-imports.d.ts
@@ -1,13 +1,15 @@
// Generated by 'unplugin-auto-import'
-// We suggest you to commit this file into source control
+export {}
declare global {
- const $: typeof import('vue/macros')['$']
const $$: typeof import('vue/macros')['$$']
+ const $: typeof import('vue/macros')['$']
const $computed: typeof import('vue/macros')['$computed']
const $customRef: typeof import('vue/macros')['$customRef']
const $ref: typeof import('vue/macros')['$ref']
const $shallowRef: typeof import('vue/macros')['$shallowRef']
const $toRef: typeof import('vue/macros')['$toRef']
+ const EffectScope: typeof import('vue')['EffectScope']
+ const ElMessage: typeof import('element-plus/es')['ElMessage']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const computed: typeof import('vue')['computed']
@@ -15,6 +17,7 @@ declare global {
const computedEager: typeof import('@vueuse/core')['computedEager']
const computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
+ const confirmAsync: typeof import('~/utils/message')['confirmAsync']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
@@ -31,8 +34,7 @@ declare global {
const defineComponent: typeof import('vue')['defineComponent']
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
const effectScope: typeof import('vue')['effectScope']
- const EffectScope: typeof import('vue')['EffectScope']
- const ElMessage: typeof import('element-plus/es')['ElMessage']
+ const errorMsg: typeof import('~/utils/message')['errorMsg']
const extendRef: typeof import('@vueuse/core')['extendRef']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
@@ -40,6 +42,8 @@ declare global {
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: typeof import('vue')['inject']
const isDefined: typeof import('@vueuse/core')['isDefined']
+ const isProxy: typeof import('vue')['isProxy']
+ const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const logicAnd: typeof import('@vueuse/core')['logicAnd']
@@ -84,6 +88,7 @@ declare global {
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
+ const successMsg: typeof import('~/utils/message')['successMsg']
const syncRef: typeof import('@vueuse/core')['syncRef']
const syncRefs: typeof import('@vueuse/core')['syncRefs']
const templateRef: typeof import('@vueuse/core')['templateRef']
@@ -125,8 +130,8 @@ declare global {
const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
const useDebounce: typeof import('@vueuse/core')['useDebounce']
- const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
+ const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
@@ -208,8 +213,8 @@ declare global {
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
const useThrottle: typeof import('@vueuse/core')['useThrottle']
- const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
+ const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
@@ -220,10 +225,10 @@ declare global {
const useTransition: typeof import('@vueuse/core')['useTransition']
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
- const useVibrate: typeof import('@vueuse/core')['useVibrate']
- const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useVModel: typeof import('@vueuse/core')['useVModel']
const useVModels: typeof import('@vueuse/core')['useVModels']
+ const useVibrate: typeof import('@vueuse/core')['useVibrate']
+ const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
@@ -232,6 +237,7 @@ declare global {
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
+ const warnMsg: typeof import('~/utils/message')['warnMsg']
const watch: typeof import('vue')['watch']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
@@ -239,8 +245,10 @@ declare global {
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
const watchOnce: typeof import('@vueuse/core')['watchOnce']
const watchPausable: typeof import('@vueuse/core')['watchPausable']
+ const watchPostEffect: typeof import('vue')['watchPostEffect']
+ const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
+ const withConfirm: typeof import('~/utils/message')['withConfirm']
}
-export {}
diff --git a/packages/web/package.json b/packages/web/package.json
index 872c6c4c4fc..643571b0e86 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -17,12 +17,15 @@
"axios": "^0.27.2",
"dayjs": "^1.11.2",
"element-plus": "^2.2.0",
+ "get-user-locale": "^1.4.0",
"laf-client-sdk": "^0.8.0-alpha.9",
+ "lodash": "^4.17.21",
"nprogress": "^0.2.0",
"pinia": "^2.0.14",
"vite-plugin-vue-layouts": "^0.6.0",
"vue": "^3.2.33",
"vue-i18n": "^9.1.10",
+ "vue-request": "^1.2.4",
"vue-router": "^4.0.15"
},
"devDependencies": {
@@ -40,8 +43,8 @@
"postcss-nested": "^5.0.6",
"typescript": "^4.6.4",
"unocss": "^0.33.2",
- "unplugin-auto-import": "^0.7.1",
- "unplugin-vue-components": "^0.19.5",
+ "unplugin-auto-import": "^0.8.7",
+ "unplugin-vue-components": "^0.19.6",
"vite": "^2.9.8",
"vite-plugin-pages": "^0.23.0",
"vitest": "^0.12.4",
diff --git a/packages/web/src/App.vue b/packages/web/src/App.vue
index 4bc8d8dfdc0..d537a30f510 100644
--- a/packages/web/src/App.vue
+++ b/packages/web/src/App.vue
@@ -1,3 +1,9 @@
+
+
diff --git a/packages/web/src/layout/components/LanguageMenu.vue b/packages/web/src/layout/components/LanguageMenu.vue
index 9f0ecb30ac4..ddedbb5414e 100644
--- a/packages/web/src/layout/components/LanguageMenu.vue
+++ b/packages/web/src/layout/components/LanguageMenu.vue
@@ -1,15 +1,17 @@
@@ -23,7 +25,7 @@ const langText = computed(() => {
-
+
{{ lang.text }}
diff --git a/packages/web/src/modules/locales.ts b/packages/web/src/modules/locales.ts
index 4aa2c2c4568..018991e6ed5 100644
--- a/packages/web/src/modules/locales.ts
+++ b/packages/web/src/modules/locales.ts
@@ -1,5 +1,8 @@
import { createI18n } from 'vue-i18n'
import messages from '@intlify/vite-plugin-vue-i18n/messages'
+import { getUserLocales } from 'get-user-locale'
+import { intersection } from 'lodash'
+import { useConfigStore } from '~/store'
export const i18n = createI18n({
locale: 'en',
@@ -18,4 +21,24 @@ export const supportLanguages = [
},
]
+export const supportLanguageNames = supportLanguages.map(item => item.name)
+
export const t = i18n.global.t.bind(i18n.global)
+
+// 检测浏览器中的默认语言
+export const userLocales = getUserLocales()
+
+// 共有的语言
+const commonLanguages = intersection(userLocales, supportLanguageNames)
+
+// 最佳的默认语言
+export const bestDefaultLanguage = commonLanguages.length > 0 ? commonLanguages[0] : 'en'
+
+export function initLanguageModule() {
+ // 同步语言设置
+ const { locale } = useI18n()
+ const config = useConfigStore()
+ watchEffect(() => {
+ locale.value = config.local.language
+ })
+}
diff --git a/packages/web/src/store/config.ts b/packages/web/src/store/config.ts
new file mode 100644
index 00000000000..f5e48f63521
--- /dev/null
+++ b/packages/web/src/store/config.ts
@@ -0,0 +1,13 @@
+import { acceptHMRUpdate, defineStore } from 'pinia'
+import { bestDefaultLanguage } from '~/modules/locales'
+
+export const useConfigStore = defineStore('config', () => {
+ const local = useLocalStorage('laf-config-local', {
+ language: bestDefaultLanguage,
+ })
+
+ return { local }
+})
+
+if (import.meta.hot)
+ import.meta.hot.accept(acceptHMRUpdate(useConfigStore, import.meta.hot))
diff --git a/packages/web/src/store/index.ts b/packages/web/src/store/index.ts
index c3a9c65bfc2..a2583927259 100644
--- a/packages/web/src/store/index.ts
+++ b/packages/web/src/store/index.ts
@@ -1 +1,2 @@
export * from './user'
+export * from './config'