Add i18n support

This commit is contained in:
2025-04-28 11:49:58 +08:00
parent 618bae3afe
commit 0d3df25a94
8 changed files with 440 additions and 16 deletions

View File

@@ -1,35 +1,59 @@
<script setup lang="ts">
import {useEditorStore} from '@/stores/editorStore';
import {useConfigStore} from '@/stores/configStore';
import {useLogStore} from '@/stores/logStore';
import { useI18n } from 'vue-i18n';
import { ref } from 'vue';
import {SUPPORTED_LOCALES, setLocale, SupportedLocaleType} from '@/i18n';
const editorStore = useEditorStore();
const configStore = useConfigStore();
const logStore = useLogStore();
const { t, locale } = useI18n();
// 语言下拉菜单
const showLanguageMenu = ref(false);
// 切换语言
const changeLanguage = (localeCode: SupportedLocaleType) => {
setLocale(localeCode);
showLanguageMenu.value = false;
};
// 切换语言菜单显示
const toggleLanguageMenu = () => {
showLanguageMenu.value = !showLanguageMenu.value;
};
</script>
<template>
<div class="toolbar-container">
<div class="statistics">
<span class="stat-item" title="行数">Ln: <span class="stat-value">{{
<span class="stat-item" :title="t('toolbar.editor.lines')">{{ t('toolbar.editor.lines') }}: <span class="stat-value">{{
editorStore.documentStats.lines
}}</span></span>
<span class="stat-item" title="字符数">Ch: <span class="stat-value">{{
<span class="stat-item" :title="t('toolbar.editor.characters')">{{ t('toolbar.editor.characters') }}: <span class="stat-value">{{
editorStore.documentStats.characters
}}</span></span>
<span class="stat-item" title="选中字符数" v-if="editorStore.documentStats.selectedCharacters > 0">
Sel: <span class="stat-value">{{ editorStore.documentStats.selectedCharacters }}</span>
<span class="stat-item" :title="t('toolbar.editor.selected')" v-if="editorStore.documentStats.selectedCharacters > 0">
{{ t('toolbar.editor.selected') }}: <span class="stat-value">{{ editorStore.documentStats.selectedCharacters }}</span>
</span>
<span v-if="logStore.showLog && logStore.latestLog" class="log-item" :class="'log-' + logStore.latestLog.level"
@click="logStore.hideCurrentLog()">
{{ logStore.latestLog.message }}
</span>
</div>
<div class="actions">
<span class="font-size" title="字体大小 (Ctrl+滚轮调整)" @click="configStore.resetFontSize">
<span class="font-size" :title="t('toolbar.fontSizeTooltip')" @click="configStore.resetFontSize">
{{ configStore.config.fontSize }}px
</span>
<span class="tab-settings">
<label title="启用Tab键缩进" class="tab-toggle">
<label :title="t('toolbar.tabLabel')" class="tab-toggle">
<input type="checkbox" :checked="configStore.config.enableTabIndent" @change="configStore.toggleTabIndent"/>
<span>Tab</span>
<span>{{ t('toolbar.tabLabel') }}</span>
</label>
<span class="tab-type" title="Tab类型切换" @click="configStore.toggleTabType">
{{ configStore.config.tabType === 'spaces' ? '空格' : '制表符' }}
<span class="tab-type" :title="t('toolbar.tabType.' + (configStore.config.tabType === 'spaces' ? 'spaces' : 'tab'))" @click="configStore.toggleTabType">
{{ t('toolbar.tabType.' + (configStore.config.tabType === 'spaces' ? 'spaces' : 'tab')) }}
</span>
<span class="tab-size" title="Tab大小" v-if="configStore.config.tabType === 'spaces'">
<button class="tab-btn" @click="configStore.decreaseTabSize" :disabled="configStore.config.tabSize <= configStore.MIN_TAB_SIZE">-</button>
@@ -37,8 +61,28 @@ const configStore = useConfigStore();
<button class="tab-btn" @click="configStore.increaseTabSize" :disabled="configStore.config.tabSize >= configStore.MAX_TAB_SIZE">+</button>
</span>
</span>
<span class="encoding">{{ configStore.config.encoding }}</span>
<button class="settings-btn">
<span class="encoding">{{ t('toolbar.encoding') }}</span>
<!-- 语言切换按钮 -->
<div class="language-selector">
<button class="language-btn" @click="toggleLanguageMenu">
{{ locale }}
<span class="arrow-up"></span>
</button>
<div class="language-menu" v-if="showLanguageMenu">
<div
v-for="lang in SUPPORTED_LOCALES"
:key="lang.code"
class="language-option"
:class="{ active: locale === lang.code }"
@click="changeLanguage(lang.code)"
>
{{ t(`languages.${lang.code}`) }}
</div>
</div>
</div>
<button class="settings-btn" :title="t('toolbar.settings')">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"></circle>
@@ -61,6 +105,7 @@ const configStore = useConfigStore();
height: 28px;
font-size: 12px;
border-top: 1px solid var(--border-color);
user-select: none;
.statistics {
display: flex;
@@ -73,6 +118,24 @@ const configStore = useConfigStore();
color: #e0e0e0;
}
}
.log-item {
cursor: default;
font-size: 12px;
transition: opacity 0.3s ease;
&.log-info {
color: rgba(177, 176, 176, 0.8);
}
&.log-warning {
color: rgba(240, 230, 140, 0.8);
}
&.log-error {
color: rgba(255, 107, 107, 0.8);
}
}
}
.actions {
@@ -142,6 +205,61 @@ const configStore = useConfigStore();
color: var(--text-muted);
font-size: 11px;
}
/* 语言切换样式 */
.language-selector {
position: relative;
.language-btn {
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
font-size: 11px;
display: flex;
align-items: center;
gap: 2px;
padding: 2px 4px;
border-radius: 3px;
&:hover {
background-color: rgba(255, 255, 255, 0.05);
}
.arrow-up {
font-size: 8px;
margin-left: 2px;
}
}
.language-menu {
position: absolute;
bottom: 100%;
right: 0;
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 3px;
margin-bottom: 4px;
min-width: 100px;
z-index: 1000;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
.language-option {
padding: 4px 8px;
cursor: pointer;
font-size: 11px;
white-space: nowrap;
&:hover {
background-color: var(--bg-hover);
}
&.active {
color: #b5cea8;
}
}
}
}
.settings-btn {
background: none;