Feature: 添加多语言支持 & 为语言选择添加菜单
This commit is contained in:
@ -17,7 +17,76 @@
|
|||||||
|
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
|
|
||||||
<v-btn icon="mdi-translate" @click="toggleLanguage" />
|
<v-menu
|
||||||
|
v-model="languageMenu"
|
||||||
|
:close-on-content-click="true"
|
||||||
|
location="bottom end"
|
||||||
|
offset="8"
|
||||||
|
>
|
||||||
|
<template #activator="{ props }">
|
||||||
|
<v-btn
|
||||||
|
v-bind="props"
|
||||||
|
:icon="$vuetify.display.xs"
|
||||||
|
:prepend-icon="$vuetify.display.xs ? undefined : 'mdi-translate'"
|
||||||
|
:size="$vuetify.display.xs ? 'default' : 'large'"
|
||||||
|
variant="text"
|
||||||
|
>
|
||||||
|
<v-icon v-if="$vuetify.display.xs">mdi-translate</v-icon>
|
||||||
|
<span v-else class="text-button">{{ currentLanguage.label }}</span>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- <v-list
|
||||||
|
class="language-menu"
|
||||||
|
density="compact"
|
||||||
|
min-width="160"
|
||||||
|
>
|
||||||
|
<v-list-item
|
||||||
|
v-for="lang in availableLanguages"
|
||||||
|
:key="lang.code"
|
||||||
|
:activate="locale === lang.code"
|
||||||
|
:title="lang.label"
|
||||||
|
@click="changeLanguage(lang.code)"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<v-icon
|
||||||
|
v-if="locale === lang.code"
|
||||||
|
class="text-primary"
|
||||||
|
icon="mdi-check"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list> -->
|
||||||
|
<div class="language-pill-container">
|
||||||
|
<v-chip-group
|
||||||
|
v-model="selectedLanguageIndex"
|
||||||
|
direction="vertical"
|
||||||
|
selected-class="text-primary"
|
||||||
|
@update:model-value="handleLanguageChange"
|
||||||
|
>
|
||||||
|
<v-chip
|
||||||
|
v-for="(lang, index) in availableLanguages"
|
||||||
|
:key="lang.code"
|
||||||
|
class="language-pill-large"
|
||||||
|
:color="locale === lang.code ? 'primary' : undefined"
|
||||||
|
label
|
||||||
|
rounded="xl"
|
||||||
|
size="large"
|
||||||
|
:value="index"
|
||||||
|
:variant="locale === lang.code ? 'flat' : 'outlined'"
|
||||||
|
>
|
||||||
|
<v-icon
|
||||||
|
v-if="locale === lang.code"
|
||||||
|
class="mr-2"
|
||||||
|
icon="mdi-check"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
{{ lang.label }}
|
||||||
|
</v-chip>
|
||||||
|
</v-chip-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</v-menu>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
<v-navigation-drawer
|
<v-navigation-drawer
|
||||||
@ -166,6 +235,7 @@
|
|||||||
const windowWidth = ref(typeof window === 'undefined' ? 1200 : window.innerWidth)
|
const windowWidth = ref(typeof window === 'undefined' ? 1200 : window.innerWidth)
|
||||||
|
|
||||||
const showAboutDialog = ref(false)
|
const showAboutDialog = ref(false)
|
||||||
|
const languageMenu = ref(false)
|
||||||
|
|
||||||
// 应用信息
|
// 应用信息
|
||||||
const appInfo = computed(() => {
|
const appInfo = computed(() => {
|
||||||
@ -254,6 +324,42 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const availableLanguages = [
|
||||||
|
{
|
||||||
|
code: 'zh',
|
||||||
|
label: '中文(简体)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'en',
|
||||||
|
label: 'English',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'ru',
|
||||||
|
label: 'Русский(Developing)',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// code: 'sp',
|
||||||
|
// label: 'Español',
|
||||||
|
// },
|
||||||
|
]
|
||||||
|
const currentLanguage = computed(() => {
|
||||||
|
return availableLanguages.find(lang => lang.code === locale.value) || availableLanguages[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectedLanguageIndex = computed(() => {
|
||||||
|
return availableLanguages.findIndex(lang => lang.code === locale.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
function changeLanguage (langCode: string) {
|
||||||
|
locale.value = langCode
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLanguageChange (index: number) {
|
||||||
|
if (index !== undefined && index >= 0 && index < availableLanguages.length) {
|
||||||
|
locale.value = availableLanguages[index].code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function toggleLanguage () {
|
function toggleLanguage () {
|
||||||
locale.value = locale.value === 'zh' ? 'en' : 'zh'
|
locale.value = locale.value === 'zh' ? 'en' : 'zh'
|
||||||
}
|
}
|
||||||
@ -360,4 +466,111 @@
|
|||||||
margin: 2px 4px !important;
|
margin: 2px 4px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 无背景的语言选择器容器 */
|
||||||
|
.language-pill-container {
|
||||||
|
padding: 8px;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大尺寸语言药丸样式 */
|
||||||
|
.language-pill-large {
|
||||||
|
margin: 4px 0;
|
||||||
|
padding: 12px 20px;
|
||||||
|
height: 48px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border-radius: 24px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-pill-large:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式药丸样式 */
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
.language-pill-container {
|
||||||
|
min-width: 160px;
|
||||||
|
max-width: 280px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-pill-large {
|
||||||
|
margin: 2px 0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-button {
|
||||||
|
font-size: 0.75rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 600px) and (max-width: 959px) {
|
||||||
|
.language-pill-large {
|
||||||
|
padding: 10px 18px;
|
||||||
|
height: 44px;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 药丸选中状态 */
|
||||||
|
.v-chip--variant-flat {
|
||||||
|
background: rgb(var(--v-theme-primary)) !important;
|
||||||
|
color: rgb(var(--v-theme-surface)) !important;
|
||||||
|
box-shadow: 0 2px 8px rgba(var(--v-theme-primary), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 药丸未选中状态 */
|
||||||
|
.v-chip--variant-outlined {
|
||||||
|
border: 1px solid rgba(var(--v-theme-info), 0.3);
|
||||||
|
color: rgb(var(--v-theme-info));
|
||||||
|
background: rgba(var(--v-theme-surface), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-chip--variant-outlined:hover {
|
||||||
|
background: rgba(var(--v-theme-info), 0.08);
|
||||||
|
border-color: rgba(var(--v-theme-info), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中状态的图标 */
|
||||||
|
.language-pill-large .v-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字体权重调整 */
|
||||||
|
.language-pill-large .v-chip__content {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-pill {
|
||||||
|
margin: 2px 4px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-pill:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式药丸样式 */
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
.language-pill-menu {
|
||||||
|
min-width: 160px;
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-pill {
|
||||||
|
margin: 1px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-button {
|
||||||
|
font-size: 0.75rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
75
src/locale/ru.json
Normal file
75
src/locale/ru.json
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"appTitle": "Paper Tube Production Calculator",
|
||||||
|
"beltSpecificationCalculate": "Belt Specification",
|
||||||
|
"multiLayerPaperTapeWidthAngleCalculate": "MultiLayer Paper Tape Width & Angle",
|
||||||
|
"paperCoreDiameter": "Paper core diameter",
|
||||||
|
"paperDensity": "Paper density",
|
||||||
|
"paperGrammage": "Grammage",
|
||||||
|
"paperRollExternalDiameter": "Paper roll external diameter",
|
||||||
|
"paperRollLength": "Paper roll length",
|
||||||
|
"paperRollWallThickness": "Paper roll wall thickness",
|
||||||
|
"paperRollWeightLengthCalculate": "Paper Roll Weight Length",
|
||||||
|
"paperTapeWidthAngleCalculate": "Paper Tape Width & Angle",
|
||||||
|
"paperTubeProductionCalculate": "Paper Tube Production",
|
||||||
|
"paperTubeWeightCalculate": "Paper Tube Weight",
|
||||||
|
"parameters": "Parameters",
|
||||||
|
"productionAmount": "Production amount",
|
||||||
|
"reset": "Reset",
|
||||||
|
"results": "Results",
|
||||||
|
"singlePaperTubeWeight": "Single paper tube weight",
|
||||||
|
"totalPaperTubeWeight": "Total paper tube weight",
|
||||||
|
"paperRollWidth": "Paper roll width",
|
||||||
|
"paperThickness": "Paper thickness",
|
||||||
|
"paperRollWeight": "Paper roll weight",
|
||||||
|
"paperLength": "Paper Roll Length",
|
||||||
|
"innerPaperWidth": "Inner paper width",
|
||||||
|
"workFrequency": "Work Frequency",
|
||||||
|
"workTime": "Work time",
|
||||||
|
"workEfficiency": "Work efficiency",
|
||||||
|
"feedPaperSpeed": "Feed speed",
|
||||||
|
"outputSpeed": "Output speed",
|
||||||
|
"productionAmountPerDay": "Daily output",
|
||||||
|
"productionAmountPerHour": "Hourly output",
|
||||||
|
"productionWeightPerDay": "Daily output",
|
||||||
|
"productionWeightPerHour": "Hourly output",
|
||||||
|
"beltAngle": "Belt angle",
|
||||||
|
"paperHolderAngle": "Paper holder angle",
|
||||||
|
"leadingLength": "Leading length",
|
||||||
|
"paperWidth": "Paper width",
|
||||||
|
"machineModel": "Model",
|
||||||
|
"maxWheelbase": "Maximum wheelbase",
|
||||||
|
"hubDiameter": "Hub diameter",
|
||||||
|
"50_120Series": "50, 120 series",
|
||||||
|
"200_Series": "200 series",
|
||||||
|
"600_Series": "600 series",
|
||||||
|
"new_200_Series": "New 200 series",
|
||||||
|
"custom": "Customize",
|
||||||
|
"recommendBeltLength": "Recommended belt length",
|
||||||
|
"recommendBeltWidth": "Recommended bandwidth",
|
||||||
|
"recommendBeltThickness": "Recommended belt thickness",
|
||||||
|
"bottomPaperWidth": "Bottom paper width",
|
||||||
|
"save": "Save",
|
||||||
|
"clear": "Clear",
|
||||||
|
"remove": "Remove",
|
||||||
|
"add": "Add",
|
||||||
|
"layer": "Layer",
|
||||||
|
"angle": "Angle",
|
||||||
|
"new_120Series": "New 120 Series",
|
||||||
|
"cumulativeThickness": "Cumulative thickness",
|
||||||
|
"reference": "Reference",
|
||||||
|
"minimum": "Minimum",
|
||||||
|
"maximum": "Maximum",
|
||||||
|
"calculatedValue": "Calculated value",
|
||||||
|
"fit": "FIT",
|
||||||
|
"notFit": "NOT FIT",
|
||||||
|
"multiLayerExcelOutputFile": "MultiLayerPaperTapeWidthAngle",
|
||||||
|
"about": "About",
|
||||||
|
"calculator": "Calculator",
|
||||||
|
"companyName": "Zhejiang Jinshen Machinery Manufacturing Co., Ltd.",
|
||||||
|
"appDescription": "Paper tube production auxiliary production tool provides calculation of various parameters such as weight, size, angle, etc.",
|
||||||
|
"allRightsReserved": "All Rights Reserved",
|
||||||
|
"close": "Close",
|
||||||
|
"officialWebsite": "Official website",
|
||||||
|
"loading": "Loading",
|
||||||
|
"paperTapeWidth": "Paper tape width"
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import en from '@/locale/en.json'
|
import en from '@/locale/en.json'
|
||||||
|
import ru from '@/locale/ru.json'
|
||||||
import zh from '@/locale/zh.json'
|
import zh from '@/locale/zh.json'
|
||||||
|
|
||||||
export default createI18n({
|
export default createI18n({
|
||||||
@ -9,5 +10,6 @@ export default createI18n({
|
|||||||
messages: {
|
messages: {
|
||||||
zh,
|
zh,
|
||||||
en,
|
en,
|
||||||
|
ru,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user