feat(i18n): Implement internationalization support for your application
- Integrate the vue-i18n plugin and configure multilingual support - Add English and Chinese translation files
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@tauri-apps/plugin-sql": "~2",
|
||||
"vue": "^3.5.21",
|
||||
"vue-i18n": "^11.1.12",
|
||||
"vue-router": "^4.5.1",
|
||||
"vuetify": "^3.10.1",
|
||||
"zod": "^4.1.12"
|
||||
|
||||
15
src/App.vue
15
src/App.vue
@@ -14,7 +14,7 @@
|
||||
<v-list-item
|
||||
prepend-avatar="/src/assets/logo.svg"
|
||||
subtitle=""
|
||||
title="Spary"
|
||||
:title="$t('app.title')"
|
||||
></v-list-item>
|
||||
</v-list>
|
||||
|
||||
@@ -29,16 +29,20 @@
|
||||
></v-list-item>
|
||||
<v-list-item
|
||||
prepend-icon="mdi-airport"
|
||||
title="Nodes"
|
||||
:title="$t('app.nodes')"
|
||||
value="nodes"
|
||||
@click="router.push('/nodes')"
|
||||
></v-list-item>
|
||||
<v-list-item
|
||||
prepend-icon="mdi-cog"
|
||||
title="Settings"
|
||||
:title="$t('app.settings')"
|
||||
value="settings"
|
||||
@click="router.push('/settings')"
|
||||
></v-list-item>
|
||||
>
|
||||
<template v-slot:append>
|
||||
<language-switcher />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
|
||||
@@ -54,8 +58,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import LanguageSwitcher from './components/LanguageSwitcher.vue'
|
||||
import notificationProvider from './components/notify/notificationProvider.vue'
|
||||
|
||||
const drawer = ref(true)
|
||||
const router = useRouter()
|
||||
useI18n()
|
||||
</script>
|
||||
|
||||
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@@ -10,6 +10,7 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AddGroup: typeof import('./components/nodeEdit/addGroup.vue')['default']
|
||||
AddNode: typeof import('./components/nodeEdit/addNode.vue')['default']
|
||||
LanguageSwitcher: typeof import('./components/LanguageSwitcher.vue')['default']
|
||||
NodeList: typeof import('./components/nodeEdit/nodeList.vue')['default']
|
||||
NodesFloatButton: typeof import('./components/nodeEdit/nodesFloatButton.vue')['default']
|
||||
NotificationProvider: typeof import('./components/notify/notificationProvider.vue')['default']
|
||||
|
||||
48
src/components/LanguageSwitcher.vue
Normal file
48
src/components/LanguageSwitcher.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
icon
|
||||
>
|
||||
<v-icon>mdi-translate</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="(item, i) in languages"
|
||||
:key="i"
|
||||
@click="changeLanguage(item.code)"
|
||||
:active="currentLanguage === item.code"
|
||||
>
|
||||
<v-list-item-title>{{ item.name }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { locale } = useI18n();
|
||||
|
||||
const languages = [
|
||||
{ code: 'en', name: 'English' },
|
||||
{ code: 'zh', name: '中文' }
|
||||
];
|
||||
|
||||
const currentLanguage = computed(() => locale.value);
|
||||
|
||||
const changeLanguage = (lang: string) => {
|
||||
locale.value = lang;
|
||||
// Store the selected language in localStorage so it persists across sessions
|
||||
localStorage.setItem('language', lang);
|
||||
};
|
||||
|
||||
// Set the initial language from localStorage if available
|
||||
const savedLanguage = localStorage.getItem('language');
|
||||
if (savedLanguage && languages.some(lang => lang.code === savedLanguage)) {
|
||||
locale.value = savedLanguage;
|
||||
}
|
||||
</script>
|
||||
@@ -2,7 +2,7 @@
|
||||
<v-container class="fill-height d-flex align-center justify-center">
|
||||
<div>
|
||||
<v-switch
|
||||
:label="String(functionStatus)"
|
||||
:label="functionStatus === 'On' ? $t('spary.functionStatus.on') : $t('spary.functionStatus.off')"
|
||||
:model-value="functionStatus === 'On'"
|
||||
@update:model-value="toggleFunctionStatus"
|
||||
></v-switch>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, computed} from 'vue'
|
||||
import {invoke} from '@tauri-apps/api/core'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {groupRepository} from "@/entities/group.ts";
|
||||
import {notify} from "@/components/notify/notifyStore.ts";
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const groupName = ref('')
|
||||
const groupNameRule = [
|
||||
(value: string): boolean | string => {
|
||||
if (value?.length >= 1 && value?.length <= 15) return true
|
||||
return 'Group name must be between 1 and 15 characters.'
|
||||
return t('addGroup.groupNameRule')
|
||||
},
|
||||
]
|
||||
|
||||
@@ -22,7 +25,7 @@ const groupSubscribeUrlRule = [
|
||||
new URL(value)
|
||||
return true
|
||||
} catch (e) {
|
||||
return 'URL is not valid.'
|
||||
return t('addGroup.urlInvalid')
|
||||
}
|
||||
},
|
||||
]
|
||||
@@ -41,7 +44,7 @@ async function addGroup() {
|
||||
const check_repeat_one = await groupRepository.findByName(groupName.value)
|
||||
console.log(check_repeat_one)
|
||||
if (check_repeat_one.length > 0) {
|
||||
notify("Group already exists.", {
|
||||
notify(t('addGroup.groupExists'), {
|
||||
color: "error"
|
||||
})
|
||||
return
|
||||
@@ -55,7 +58,7 @@ async function addGroup() {
|
||||
arguments: groupArguments.value
|
||||
}
|
||||
)
|
||||
notify("Group added.")
|
||||
notify(t('addGroup.groupAdded'))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,17 +80,17 @@ async function add_group(groupName: string, groupSubscribeUrl: string | null, gr
|
||||
<v-text-field
|
||||
v-model="groupName"
|
||||
:rules="groupNameRule"
|
||||
label="Group name"
|
||||
:label="$t('addGroup.groupName')"
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field
|
||||
v-model="groupSubscribeUrl"
|
||||
:rules="groupSubscribeUrlRule"
|
||||
label="Subscribe URL"
|
||||
:label="$t('addGroup.subscribeUrl')"
|
||||
></v-text-field>
|
||||
<v-textarea
|
||||
v-model="groupArguments"
|
||||
label="Arguments"
|
||||
:label="$t('addGroup.arguments')"
|
||||
></v-textarea>
|
||||
<v-btn
|
||||
class="mt-2"
|
||||
@@ -96,7 +99,7 @@ async function add_group(groupName: string, groupSubscribeUrl: string | null, gr
|
||||
:disabled="isAddDisabled"
|
||||
:loading="isAdding"
|
||||
@click="addGroup">
|
||||
Add
|
||||
{{ $t('addGroup.addGroupButton') }}
|
||||
</v-btn>
|
||||
</v-form>
|
||||
</v-sheet>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, onMounted, computed} from 'vue'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {Group, groupRepository} from "@/entities/group.ts";
|
||||
import {nodeRepository} from "@/entities/node.ts";
|
||||
import {notify} from "@/components/notify/notifyStore.ts";
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{ groupId: string }>()
|
||||
|
||||
const allGroups = ref<Group[]>([])
|
||||
@@ -36,7 +39,7 @@ async function addNode() {
|
||||
arguments: nodeArguments.value,
|
||||
group_id: selectedGroupId.value
|
||||
})
|
||||
notify("Node added successfully")
|
||||
notify(t('addNode.nodeAddedSuccess'))
|
||||
isAdding.value = false
|
||||
}
|
||||
}
|
||||
@@ -47,7 +50,7 @@ async function addNode() {
|
||||
<v-form fast-fail @submit.prevent>
|
||||
<v-select
|
||||
v-model="selectedGroupId"
|
||||
label="Group"
|
||||
:label="$t('addNode.group')"
|
||||
:items="allGroups"
|
||||
item-title="name"
|
||||
item-value="id"
|
||||
@@ -56,12 +59,12 @@ async function addNode() {
|
||||
|
||||
<v-text-field
|
||||
v-model="nodeAlias"
|
||||
label="Node alias"
|
||||
:label="$t('addNode.nodeAlias')"
|
||||
></v-text-field>
|
||||
|
||||
<v-textarea
|
||||
v-model="nodeArguments"
|
||||
label="Arguments"
|
||||
:label="$t('addNode.arguments')"
|
||||
></v-textarea>
|
||||
<v-btn
|
||||
class="mt-2"
|
||||
@@ -70,7 +73,7 @@ async function addNode() {
|
||||
:disabled="isAddDisabled"
|
||||
:loading="isAdding"
|
||||
@click="addNode">
|
||||
Add
|
||||
{{ $t('addNode.addNodeButton') }}
|
||||
</v-btn>
|
||||
</v-form>
|
||||
</v-sheet>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import {ref} from 'vue'
|
||||
import {useRouter} from "vue-router";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {groupRepository} from "@/entities/group.ts";
|
||||
import {nodeRepository} from "@/entities/node.ts";
|
||||
|
||||
const router = useRouter()
|
||||
useI18n()
|
||||
|
||||
interface GroupItem {
|
||||
name: string
|
||||
@@ -69,7 +71,7 @@ loadData()
|
||||
prepend-icon="mdi-plus"
|
||||
@click.stop="addItem(group)"
|
||||
>
|
||||
add
|
||||
{{ $t('nodeList.add') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-expansion-panel-title>
|
||||
@@ -90,7 +92,7 @@ loadData()
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div>{{ item.url }}</div>
|
||||
<div>usage: {{ item.traffic }}</div>
|
||||
<div>{{ $t('nodeList.usage', { traffic: item.traffic }) }}</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import {useRouter} from "vue-router";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const router = useRouter()
|
||||
useI18n()
|
||||
|
||||
</script>
|
||||
|
||||
@@ -15,13 +17,13 @@ const router = useRouter()
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="success"></v-icon>
|
||||
</template>
|
||||
add group
|
||||
{{ $t('nodesFloatButton.addGroup') }}
|
||||
</v-btn>
|
||||
<v-btn key="2" prepend-icon="mdi-airplane-marker">
|
||||
<template v-slot:prepend>
|
||||
<v-icon color="success"></v-icon>
|
||||
</template>
|
||||
add node
|
||||
{{ $t('nodesFloatButton.addNode') }}
|
||||
</v-btn>
|
||||
</v-speed-dial>
|
||||
</div>
|
||||
|
||||
56
src/locales/en.json
Normal file
56
src/locales/en.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"app": {
|
||||
"title": "Spary",
|
||||
"nodes": "Nodes",
|
||||
"settings": "Settings",
|
||||
"add": "Add",
|
||||
"group": "Group",
|
||||
"node": "Node"
|
||||
},
|
||||
"spary": {
|
||||
"functionStatus": {
|
||||
"on": "On",
|
||||
"off": "Off"
|
||||
}
|
||||
},
|
||||
"nodeList": {
|
||||
"add": "add",
|
||||
"usage": "usage: {traffic}",
|
||||
"noNodes": "No nodes available"
|
||||
},
|
||||
"nodesFloatButton": {
|
||||
"addGroup": "add group",
|
||||
"addNode": "add node"
|
||||
},
|
||||
"addGroup": {
|
||||
"title": "Add Group",
|
||||
"groupName": "Group name",
|
||||
"groupNameRule": "Group name must be between 1 and 15 characters.",
|
||||
"subscribeUrl": "Subscribe URL",
|
||||
"urlInvalid": "URL is not valid.",
|
||||
"arguments": "Arguments",
|
||||
"addGroupButton": "Add",
|
||||
"groupExists": "Group already exists.",
|
||||
"groupAdded": "Group added."
|
||||
},
|
||||
"addNode": {
|
||||
"title": "Add Node",
|
||||
"group": "Group",
|
||||
"nodeAlias": "Node alias",
|
||||
"arguments": "Arguments",
|
||||
"addNodeButton": "Add",
|
||||
"nodeAddedSuccess": "Node added successfully"
|
||||
},
|
||||
"common": {
|
||||
"save": "Save",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"confirm": "Confirm",
|
||||
"darkModeOn": "Dark Mode On",
|
||||
"darkModeOff": "Dark Mode Off",
|
||||
"language": "Language",
|
||||
"english": "English",
|
||||
"chinese": "Chinese"
|
||||
}
|
||||
}
|
||||
56
src/locales/zh.json
Normal file
56
src/locales/zh.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"app": {
|
||||
"title": "Spary",
|
||||
"nodes": "节点",
|
||||
"settings": "设置",
|
||||
"add": "添加",
|
||||
"group": "组",
|
||||
"node": "节点"
|
||||
},
|
||||
"spary": {
|
||||
"functionStatus": {
|
||||
"on": "开",
|
||||
"off": "关"
|
||||
}
|
||||
},
|
||||
"nodeList": {
|
||||
"add": "添加",
|
||||
"usage": "使用量: {traffic}",
|
||||
"noNodes": "没有可用节点"
|
||||
},
|
||||
"nodesFloatButton": {
|
||||
"addGroup": "添加组",
|
||||
"addNode": "添加节点"
|
||||
},
|
||||
"addGroup": {
|
||||
"title": "添加组",
|
||||
"groupName": "组名称",
|
||||
"groupNameRule": "组名称必须在1到15个字符之间。",
|
||||
"subscribeUrl": "订阅链接",
|
||||
"urlInvalid": "URL无效。",
|
||||
"arguments": "参数",
|
||||
"addGroupButton": "添加",
|
||||
"groupExists": "组已存在。",
|
||||
"groupAdded": "组已添加。"
|
||||
},
|
||||
"addNode": {
|
||||
"title": "添加节点",
|
||||
"group": "组",
|
||||
"nodeAlias": "节点别名",
|
||||
"arguments": "参数",
|
||||
"addNodeButton": "添加",
|
||||
"nodeAddedSuccess": "节点添加成功"
|
||||
},
|
||||
"common": {
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"confirm": "确认",
|
||||
"darkModeOn": "深色模式开",
|
||||
"darkModeOff": "深色模式关",
|
||||
"language": "语言",
|
||||
"english": "英语",
|
||||
"chinese": "中文"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,53 @@
|
||||
<template>
|
||||
|
||||
<v-container>
|
||||
<v-card class="mx-auto" max-width="600">
|
||||
<v-card-title>{{ $t('app.settings') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-btn @click="toggleDarkMode" disabled>
|
||||
{{ darkMode ? $t('common.darkModeOff') : $t('common.darkModeOn') }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<h3>{{ $t('common.language') }}</h3>
|
||||
<v-btn-toggle v-model="selectedLanguage" mandatory>
|
||||
<v-btn value="en">{{ $t('common.english') }}</v-btn>
|
||||
<v-btn value="zh">{{ $t('common.chinese') }}</v-btn>
|
||||
</v-btn-toggle>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
//
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { locale } = useI18n();
|
||||
const selectedLanguage = ref(locale.value);
|
||||
const darkMode = ref(false);
|
||||
|
||||
// Update the translation when the language changes
|
||||
watch(selectedLanguage, (newLang) => {
|
||||
locale.value = newLang;
|
||||
localStorage.setItem('language', newLang);
|
||||
});
|
||||
|
||||
// Initialize the selected language from localStorage
|
||||
onMounted(() => {
|
||||
const savedLanguage = localStorage.getItem('language');
|
||||
if (savedLanguage) {
|
||||
selectedLanguage.value = savedLanguage;
|
||||
locale.value = savedLanguage;
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle dark mode
|
||||
const toggleDarkMode = () => {
|
||||
darkMode.value = !darkMode.value;
|
||||
// Here you would typically update a theme preference
|
||||
};
|
||||
</script>
|
||||
|
||||
17
src/plugins/i18n.ts
Normal file
17
src/plugins/i18n.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import en from '@/locales/en.json'
|
||||
import zh from '@/locales/zh.json'
|
||||
|
||||
const messages = {
|
||||
en: en,
|
||||
zh: zh
|
||||
}
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false, // Use composition API mode
|
||||
locale: 'en', // Default locale
|
||||
fallbackLocale: 'en', // Fallback locale
|
||||
messages
|
||||
})
|
||||
|
||||
export default i18n
|
||||
@@ -7,6 +7,7 @@
|
||||
// Plugins
|
||||
import vuetify from './vuetify'
|
||||
import router from '../router'
|
||||
import i18n from './i18n'
|
||||
|
||||
// Types
|
||||
import type { App } from 'vue'
|
||||
@@ -15,4 +16,5 @@ export function registerPlugins (app: App) {
|
||||
app
|
||||
.use(vuetify)
|
||||
.use(router)
|
||||
.use(i18n)
|
||||
}
|
||||
|
||||
7
src/vue-i18n.d.ts
vendored
Normal file
7
src/vue-i18n.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/* eslint-disable */
|
||||
import Vue from "vue";
|
||||
declare module "@vue/runtime-core" {
|
||||
export interface ComponentCustomProperties {
|
||||
$t: (key: string, ...args: any[]) => string;
|
||||
}
|
||||
}
|
||||
34
yarn.lock
34
yarn.lock
@@ -304,6 +304,27 @@
|
||||
resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz"
|
||||
integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==
|
||||
|
||||
"@intlify/core-base@11.1.12":
|
||||
version "11.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-11.1.12.tgz#e02529021a4e69f8a1adcca5ce61963c71cd72ba"
|
||||
integrity sha512-whh0trqRsSqVLNEUCwU59pyJZYpU8AmSWl8M3Jz2Mv5ESPP6kFh4juas2NpZ1iCvy7GlNRffUD1xr84gceimjg==
|
||||
dependencies:
|
||||
"@intlify/message-compiler" "11.1.12"
|
||||
"@intlify/shared" "11.1.12"
|
||||
|
||||
"@intlify/message-compiler@11.1.12":
|
||||
version "11.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-11.1.12.tgz#27e69790b711a92cddb07175187dd09a7b270b55"
|
||||
integrity sha512-Fv9iQSJoJaXl4ZGkOCN1LDM3trzze0AS2zRz2EHLiwenwL6t0Ki9KySYlyr27yVOj5aVz0e55JePO+kELIvfdQ==
|
||||
dependencies:
|
||||
"@intlify/shared" "11.1.12"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@intlify/shared@11.1.12":
|
||||
version "11.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-11.1.12.tgz#ab41083e017d622cf63c7dc88a0ee0bc27f4127a"
|
||||
integrity sha512-Om86EjuQtA69hdNj3GQec9ZC0L0vPSAnXzB3gP/gyJ7+mA7t06d9aOAiqMZ+xEOsumGP4eEBlfl8zF2LOTzf2A==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.13"
|
||||
resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz"
|
||||
@@ -877,7 +898,7 @@
|
||||
"@vue/compiler-dom" "3.5.22"
|
||||
"@vue/shared" "3.5.22"
|
||||
|
||||
"@vue/devtools-api@^6.6.4":
|
||||
"@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.6.4":
|
||||
version "6.6.4"
|
||||
resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz"
|
||||
integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
|
||||
@@ -2429,7 +2450,7 @@ sisteransi@^1.0.5:
|
||||
resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz"
|
||||
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
|
||||
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.1:
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2, source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
@@ -2688,6 +2709,15 @@ vue-eslint-parser@^10.2.0:
|
||||
esquery "^1.6.0"
|
||||
semver "^7.6.3"
|
||||
|
||||
vue-i18n@^11.1.12:
|
||||
version "11.1.12"
|
||||
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-11.1.12.tgz#230b21d65dd89343ebd40d19c9e3cfb13db8c1fd"
|
||||
integrity sha512-BnstPj3KLHLrsqbVU2UOrPmr0+Mv11bsUZG0PyCOzsawCivk8W00GMXHeVUWIDOgNaScCuZah47CZFE+Wnl8mw==
|
||||
dependencies:
|
||||
"@intlify/core-base" "11.1.12"
|
||||
"@intlify/shared" "11.1.12"
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
|
||||
vue-router@^4.5.1:
|
||||
version "4.5.1"
|
||||
resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz"
|
||||
|
||||
Reference in New Issue
Block a user