Skip to content

Commit

Permalink
Implemented internationalisation of the user interface
Browse files Browse the repository at this point in the history
  • Loading branch information
lisongxuan committed Aug 19, 2024
1 parent f1c6a67 commit a5092d5
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 63 deletions.
Binary file modified apis/requirements.txt
Binary file not shown.
4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"dependencies": {
"axios": "^1.7.2",
"element-plus": "^2.6.2",
"vue": "^3.4.21"
"js-cookie": "^3.0.5",
"vue": "^3.4.21",
"vue-i18n": "^9.14.0"
},
"devDependencies": {
"@iconify-json/ep": "^1.1.15",
Expand Down
88 changes: 54 additions & 34 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,20 @@
style="width: 240px"
@change="handleInputChange"
>
<div v-for="item in options">
<el-tooltip
v-if="item.value === 'all'"
class="box-item"
effect="dark"
content="搜索所有语言,可能导致运行缓慢,请谨慎使用"
placement="top"
>
<el-option
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-tooltip>
<el-option v-else
:key="item.value"
<div v-for="item in options" :key="item.value">
<el-tooltip
v-if="item.value === 'all'"
class="box-item"
effect="dark"
:content="$t('app.searchAll')"
placement="top"
>
<el-option
:label="item.label"
:value="item.value"
/>
</el-tooltip>
<el-option v-else
:label="item.label"
:value="item.value"
/>
Expand All @@ -37,13 +35,13 @@
<el-input
v-model="inputValue"
size="large"
placeholder="请输入搜索内容"
:placeholder="$t('app.jumpIntoContent')"
clearable
:suffix-icon="Search"
@input="handleInputChange"
/>
</div>
<el-button v-if="contextFlag" @click="handleReturn()" type="primary" style="margin: 10px;">返回搜索</el-button>
<el-button v-if="contextFlag" @click="handleReturn()" type="primary" style="margin: 10px;">{{$t('app.returnSearch')}}</el-button>
<el-pagination
background
layout="prev, pager, next"
Expand All @@ -57,7 +55,7 @@
<el-table-column label="位置" width="180">
<template #default="{ row }">
<div>{{ row.position }}</div>
<el-button size="small" @click="goToContext(row)">跳转至上下文</el-button>
<el-button size="small" @click="goToContext(row)">{{$t('app.jumpIntoContent')}}</el-button>
</template>
</el-table-column>
<el-table-column
Expand All @@ -77,8 +75,8 @@
FINAL FANTASY is a registered trademark of Square Enix Holdings Co., Ltd.<br>
All contents here © SQUARE ENIX
</div>
<el-dialog v-model="dialogFormVisible" title="设置" width="500">
<h3>语言/版本设置</h3>
<el-dialog v-model="dialogFormVisible" width="500":title="$t('app.setting')">
<h3>{{$t('app.languageSetting')}}</h3>
<div v-for="(item, index) in allVersions" :key="index">
<h4>{{ languages[item.language] }}</h4>
<el-checkbox-group v-model="tempSelectedVersions[item.language]">
Expand All @@ -91,20 +89,20 @@
</el-checkbox>
</el-checkbox-group>
</div>
<h4>默认搜索语言</h4>
<h4>{{$t('app.defaultLanguage')}}</h4>
<el-cascader v-model="tempDefaultLanguage" :options="languageOptions" />
<el-divider></el-divider>
<h3>显示设置</h3>
<h4>每页数据条数</h4>
<h3>{{$t('app.displaySetting')}}</h3>
<h4>{{$t('app.dataPerPage')}}</h4>
<el-input-number v-model="tempPaginationNumber" :min="2" :max="50" />
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="dialogFormVisible = false, handleConfirm()">确认</el-button>
<el-button @click="dialogFormVisible = false">{{$t('app.cancel')}}</el-button>
<el-button type="primary" @click="dialogFormVisible = false, handleConfirm()">{{$t('app.confirm')}}</el-button>
</div>
</template>
<el-divider></el-divider>
<h5>*所有设置均用Cookies存储,更改设置视为同意使用Cookies</h5>
<h5>{{$t('app.cookieConsent')}}</h5>
</el-dialog>
</el-config-provider>
</template>
Expand All @@ -116,6 +114,16 @@ import { Search } from '@element-plus/icons-vue'
import config from './config.json';
import { ca } from 'element-plus/es/locale';
import Cookies from 'js-cookie';
import { useI18n } from 'vue-i18n';
const { t ,locale} = useI18n();
onMounted(() => {
const storedPageLanguage = Cookies.get('pageLanguage');
if (storedPageLanguage) {
locale.value = JSON.parse(storedPageLanguage);
}
document.title = t('header.title');
});
const headerSelected = ref('include');
provide('headerSelected', headerSelected);
const dialogFormVisible = ref(false);
Expand Down Expand Up @@ -157,13 +165,14 @@ const inputValue = ref('')
const versions = ref([]);
const latestVersions = ref<{ [key: string]: string }[]>([]);
const languages: { [key: string]: string } = reactive({
'en': '英语',
'jp': '日语',
'cn': '中文',
'de': '德语',
'fr': '法语',
'kr': '韩语'
'en': t('app.en'),
'jp': t('app.jp'),
'cn': t('app.cn'),
'de': t('app.de'),
'fr': t('app.fr'),
'kr': t('app.kr')
});
var selectedVersions = ref<{ [key: string]: string; }[]>([]);
const tableData = ref<string[][]>([]);
const cacheRow = ref<RowType>();
Expand Down Expand Up @@ -201,11 +210,21 @@ watch(paginationNumber, (newValue) => {
watch(defaultLanguage, (newValue) => {
Cookies.set('defaultLanguage', JSON.stringify(newValue), { expires: 7 }); // cookies有效期为7天
});
watch(locale, () => {
languages.en = t('app.en');
languages.jp = t('app.jp');
languages.cn = t('app.cn');
languages.de = t('app.de');
languages.fr = t('app.fr');
languages.kr = t('app.kr');
});
// 使用计算属性来动态生成options
const options = computed(() => [
{ value: 'all', label: '全部语言' },
{ value: 'all', label: t('app.allLanguages') },
...columns.value.map(column => ({ value: column.text, label: `${column.text}` }))
]);
interface RowType {
position: string;
[key: string]: any;
Expand Down Expand Up @@ -464,6 +483,7 @@ const storedDefaultLanguage = Cookies.get('defaultLanguage');
defaultLanguage.value = JSON.parse(storedDefaultLanguage);
tempDefaultLanguage.value = JSON.parse(storedDefaultLanguage);
}
const storedSelectedVersions = Cookies.get('selectedVersions');
if (storedSelectedVersions) {
selectedVersions.value = JSON.parse(storedSelectedVersions);
Expand Down
9 changes: 0 additions & 9 deletions frontend/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,18 @@ declare module 'vue' {
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
}
}
54 changes: 36 additions & 18 deletions frontend/src/components/layouts/BaseHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<el-menu-item index="0" :to="{ path: '/' }">
<img src="/favicon.png" v-if="!isDark" style="width: 100%; height: 100%; margin-right: 8px;">
<img src="/favicon-dark.png" v-else style="width: 100%; height: 100%; margin-right: 8px;" >
最终幻想14 文本搜索器
{{ $t('header.title') }}
</el-menu-item>
</el-menu>

Expand All @@ -23,24 +23,24 @@
<el-tooltip
class="box-item"
effect="dark"
content="部分搜索:搜索结果包含输入内容"
:content="$t('header.partialDesc')"
placement="bottom-start"
>
<el-menu-item index="include">部分搜索</el-menu-item></el-tooltip>
<el-menu-item index="include">{{$t('header.partial')}}</el-menu-item></el-tooltip>
<el-tooltip
class="box-item"
effect="dark"
content="相似搜索:搜索结果包含输入内容的相似内容"
:content="$t('header.similarDesc')"
placement="bottom-start"
>
<el-menu-item index="similar">相似搜索</el-menu-item></el-tooltip>
<el-menu-item index="similar">{{$t('header.similar')}}</el-menu-item></el-tooltip>
<el-tooltip
class="box-item"
effect="dark"
content="精确搜索:搜索结果与输入内容完全匹配"
:content="$t('header.exactDesc')"
placement="bottom-start"
>
<el-menu-item index="exact">精确搜索</el-menu-item></el-tooltip>
<el-menu-item index="exact">{{$t('header.exact')}}</el-menu-item></el-tooltip>
</el-menu>

<el-menu
Expand All @@ -49,6 +49,13 @@
:ellipsis="false"
>
<div class="flex-grow" />
<el-sub-menu index="3">
<template #title>
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" width="1.2em" height="1.2em" data-v-63d067da=""><path fill="currentColor" d="m18.5 10l4.4 11h-2.155l-1.201-3h-4.09l-1.199 3h-2.154L16.5 10h2zM10 2v2h6v2h-1.968a18.222 18.222 0 0 1-3.62 6.301a14.864 14.864 0 0 0 2.336 1.707l-.751 1.878A17.015 17.015 0 0 1 9 13.725a16.676 16.676 0 0 1-6.201 3.548l-.536-1.929a14.7 14.7 0 0 0 5.327-3.042A18.078 18.078 0 0 1 4.767 8h2.24A16.032 16.032 0 0 0 9 10.877a16.165 16.165 0 0 0 2.91-4.876L2 6V4h6V2h2zm7.5 10.885L16.253 16h2.492L17.5 12.885z"></path></svg>
</template>
<el-menu-item index="3-1" @click="selectLanguage('zh')">中文</el-menu-item>
<el-menu-item index="3-2" @click="selectLanguage('en')">English</el-menu-item>
</el-sub-menu>
<el-menu-item h="full" @click="dialogFormVisible = true">
<button
class="border-none w-full bg-transparent cursor-pointer"
Expand All @@ -67,26 +74,26 @@
</el-menu-item>

<el-sub-menu index="2">
<template #title>关于</template>
<el-menu-item index="2-1"><a href="https://arkady14.me" target="_blank" class="github-link">开发:Selini@神拳痕</a></el-menu-item>
<template #title>{{$t('header.about')}}</template>
<el-menu-item index="2-1"><a href="https://arkady14.me" target="_blank" class="github-link">{{$t('header.author')}}</a></el-menu-item>
<el-menu-item index="2-2">
<a href="https://github.com/lisongxuan/FFXIV-text-search-engine" target="_blank" class="github-link">
GitHub Repository
{{$t('header.github')}}
</a>
</el-menu-item>
<el-menu-item index="2-3" @click="copyToClipboard('[email protected]')">
反馈邮箱(建议,点击复制)
{{$t('header.contactmail')}}
</el-menu-item>
<el-menu-item index="2-4" @click="copyToClipboard('719279154')">
反馈QQ(点击复制)
{{$t('header.contactqq')}}
</el-menu-item>
<el-tooltip
class="box-item"
effect="dark"
content="暂无更新日志"
:content="$t('header.temporary')"
placement="bottom-start"
>
<el-menu-item index="2-5" disabled>更新日志</el-menu-item></el-tooltip>
<el-menu-item index="2-5" disabled>{{$t('header.updatelog')}}</el-menu-item></el-tooltip>
</el-sub-menu>
</el-menu>
</div>
Expand All @@ -97,7 +104,9 @@ import { ref, inject, Ref } from 'vue'
import { ElMessage } from 'element-plus';
import { toggleDark ,isDark} from '~/composables';
import Setting from 'path/to/Setting.vue'; // Import the Setting component
import { useI18n } from 'vue-i18n';
import Cookies from 'js-cookie';
const { t ,locale} = useI18n();
const selected = inject<Ref<string>>('headerSelected', ref('include'))
const dialogFormVisible = inject<Ref<Boolean>>('dialogFormVisible', ref(false))
const handleSelect = (key: string, keyPath: string[]) => {
Expand All @@ -107,20 +116,29 @@ const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text).then(() => {
// 使用 ElMessage 显示复制成功的提示
ElMessage({
message: '复制成功',
message: t('header.copysuccess'),
type: 'success',
duration: 3000, // 持续时间,单位毫秒
});
}).catch(err => {
// 使用 ElMessage 显示复制失败的提示
ElMessage.error({
message: '复制失败',
message: t('header.copyfail'),
duration: 3000, // 持续时间,单位毫秒
});
console.error('复制失败', err);
console.error(t('header.copyfail'), err);
});
}
const selectLanguage = (lang: string) => {
locale.value = lang;
Cookies.set('pageLanguage', JSON.stringify(lang), { expires: 7 }); // cookies有效期为7天
ElMessage({
message: t('header.selectlanguagesuccess'),
type: 'success',
duration: 6000, // 持续时间,单位毫秒
});
}
</script>

<style>
Expand Down
43 changes: 43 additions & 0 deletions frontend/src/languages/en.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const en = {
header: {
title:'Final Fantasy XIV Text Searcher',
partial:'Partial Search',
partialDesc:"Partial Search: Search results contain input content",
similar:'Similar Search',
similarDesc:"Similar Search: Search results contain content similar to the input content",
exact:'Exact Search',
exactDesc:"Exact Search: Search results match the input content exactly",
about:'About',
author:'Developer: Selini-神拳痕',
github:'GitHub Repository',
contactmail:'Feedback Email (Suggested, Click to Copy)',
contactqq:'Feedback QQ (Click to Copy)',
updatelog:'Update Log',
temporary:'No Content',
copysuccess:'Copy Succeeded',
copyfail:'Copy Failed',
selectlanguagesuccess:'Language switched, please refresh the page if there is any problem',
},
app:{
searchAll:"Search across all languages, may cause slow performance, please use with caution",
pleaseTypeSearch:"Please enter search content",
returnSearch:"Return to search",
jumpIntoContent:"Jump to context",
setting:"Setting",
languageSetting:"Language/Version Setting",
defaultLanguage:"Default Search Language",
displaySetting:"Display Setting",
dataPerPage:"Data per Page",
cancel:"Cancel",
confirm:"Confirm",
cookieConsent:"*All settings are stored with Cookies, changing settings is considered as agreeing to use Cookies",
en:"English",
jp:"Japanese",
cn:"Chinese",
de:"German",
fr:"French",
kr:"Korean",
allLanguages:"All Languages",
}
}
export default en;
Loading

0 comments on commit a5092d5

Please sign in to comment.