Feature: 添加多语言支持 & 为语言选择添加菜单

This commit is contained in:
2025-07-18 11:25:12 +08:00
parent 1733d2ae5b
commit fcbf728225
3 changed files with 291 additions and 1 deletions

View File

@ -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
View 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"
}

View File

@ -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,
}, },
}) })