feat: translate

This commit is contained in:
KirisameVanilla 2025-09-22 22:22:52 +08:00
parent b44f324808
commit b5944ae7a8
No known key found for this signature in database
GPG Key ID: 7FC750F817277AC5
2 changed files with 139 additions and 25 deletions

92
src/api/translate.ts Normal file
View File

@ -0,0 +1,92 @@
import apiClient from './client'
/**
*
*/
export interface TranslateRequest {
query: string
from_lang?: 'auto' | 'fr' | 'jp' | 'zh' | 'en'
to_lang: 'fr' | 'jp' | 'zh' | 'en'
}
/**
*
*/
export interface TranslateResponse {
translated_text: string
}
/**
*
* @param params
* @returns
*/
export const translateText = async (params: TranslateRequest): Promise<TranslateResponse> => {
try {
const response = await apiClient.post<TranslateResponse>('/translate', params)
return response.data
} catch (error: any) {
console.error('Translation API error:', error)
// 处理不同类型的错误
if (error.response) {
const status = error.response.status
const message = error.response.data?.message || '翻译失败'
switch (status) {
case 401:
throw new Error('请先登录后再使用翻译功能')
case 500:
throw new Error('翻译服务暂时不可用,请稍后重试')
case 400:
throw new Error('翻译请求参数错误')
default:
throw new Error(`翻译失败:${message}`)
}
} else if (error.request) {
throw new Error('网络连接失败,请检查网络连接')
} else {
throw new Error('翻译请求失败,请稍后重试')
}
}
}
/**
*
* @param query
* @param from_lang
* @param to_lang
* @returns
*/
export const debugTranslate = async (
query: string,
from_lang: string = 'auto',
to_lang: string = 'zh'
): Promise<TranslateResponse> => {
try {
const response = await apiClient.post<TranslateResponse>('/translate/debug', null, {
params: { query, from_lang, to_lang }
})
return response.data
} catch (error: any) {
console.error('Debug translation API error:', error)
if (error.response) {
const status = error.response.status
const message = error.response.data?.message || '调试翻译失败'
switch (status) {
case 403:
throw new Error('权限不足,仅管理员可使用此功能')
case 429:
throw new Error('请求频率过高,请稍后再试')
case 401:
throw new Error('请先登录后再使用翻译功能')
default:
throw new Error(`调试翻译失败:${message}`)
}
} else {
throw new Error('调试翻译请求失败,请稍后重试')
}
}
}

View File

@ -6,8 +6,8 @@
<section class="bg-white py-12">
<div class="mx-auto px-4 max-w-[1030px]">
<div class="mb-8 text-center">
<h1 class="mb-4 font-deserta text-blue-700 text-4xl">法语翻译</h1>
<p class="font-inter text-gray-600">中法双向翻助力语言学习</p>
<h1 class="mb-4 font-deserta text-blue-700 text-4xl">多语言翻译</h1>
<p class="font-inter text-gray-600">支持中文法语日语互助力语言学习</p>
</div>
<div class="mx-auto max-w-[900px]">
@ -17,9 +17,10 @@
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-700">源语言</h3>
<select v-model="sourceLang" class="px-3 py-1 border border-gray-300 rounded">
<option value="auto">自动检测</option>
<option value="zh">中文</option>
<option value="fr">法语</option>
<option value="en"></option>
<option value="jp"></option>
</select>
</div>
<textarea
@ -36,7 +37,7 @@
<select v-model="targetLang" class="px-3 py-1 border border-gray-300 rounded">
<option value="zh">中文</option>
<option value="fr">法语</option>
<option value="en"></option>
<option value="jp"></option>
</select>
</div>
<div class="bg-white p-4 border border-gray-300 rounded-lg w-full h-[200px] overflow-y-auto">
@ -93,11 +94,11 @@
<div v-for="(item, index) in translationHistory" :key="index" class="bg-white shadow-sm p-4 rounded-lg">
<div class="gap-4 grid grid-cols-1 md:grid-cols-2">
<div>
<p class="mb-1 text-gray-500 text-sm">{{ item.sourceLang === 'zh' ? '中文' : item.sourceLang === 'fr' ? '法语' : '英语' }}</p>
<p class="mb-1 text-gray-500 text-sm">{{ getLanguageDisplayName(item.sourceLang) }}</p>
<p class="text-gray-800">{{ item.sourceText }}</p>
</div>
<div>
<p class="mb-1 text-gray-500 text-sm">{{ item.targetLang === 'zh' ? '中文' : item.targetLang === 'fr' ? '法语' : '英语' }}</p>
<p class="mb-1 text-gray-500 text-sm">{{ getLanguageDisplayName(item.targetLang) }}</p>
<p class="text-gray-800">{{ item.translationResult }}</p>
</div>
</div>
@ -121,6 +122,7 @@
import { ref, onMounted } from 'vue'
import AppHeader from '../components/AppHeader.vue'
import AppFooter from '../components/AppFooter.vue'
import { translateText, type TranslateRequest } from '../api/translate'
interface TranslationHistoryItem {
sourceText: string
@ -130,15 +132,23 @@ interface TranslationHistoryItem {
timestamp: string
}
//
const langDisplayMap: Record<string, string> = {
'zh': '中文',
'fr': '法语',
'jp': '日语',
'auto': '自动检测'
}
const sourceText = ref('')
const translationResult = ref('')
const sourceLang = ref('zh')
const targetLang = ref('fr')
const sourceLang = ref('auto')
const targetLang = ref('zh')
const loading = ref(false)
const error = ref('')
const translationHistory = ref<TranslationHistoryItem[]>([])
// API
// API
const handleTranslate = async () => {
if (!sourceText.value.trim()) return
@ -146,26 +156,27 @@ const handleTranslate = async () => {
error.value = ''
try {
// API
// 使
await new Promise(resolve => setTimeout(resolve, 1000))
//
let result = ''
if (sourceLang.value === 'zh' && targetLang.value === 'fr') {
result = `[法语翻译] ${sourceText.value}`
} else if (sourceLang.value === 'fr' && targetLang.value === 'zh') {
result = `[中文翻译] ${sourceText.value}`
} else {
result = `[翻译结果] ${sourceText.value}`
// API
const translateParams: TranslateRequest = {
query: sourceText.value.trim(),
from_lang: sourceLang.value as 'auto' | 'fr' | 'jp' | 'zh',
to_lang: targetLang.value as 'fr' | 'jp' | 'zh'
}
translationResult.value = result
// auto
if (targetLang.value === 'auto') {
error.value = '目标语言不能为自动检测,请选择具体语言'
return
}
// API
const result = await translateText(translateParams)
translationResult.value = result.translated_text
//
const historyItem: TranslationHistoryItem = {
sourceText: sourceText.value,
translationResult: result,
translationResult: result.translated_text,
sourceLang: sourceLang.value,
targetLang: targetLang.value,
timestamp: new Date().toLocaleString('zh-CN')
@ -178,13 +189,19 @@ const handleTranslate = async () => {
} catch (err: any) {
console.error('Translation error:', err)
error.value = '翻译失败,请稍后重试'
error.value = err.message || '翻译失败,请稍后重试'
} finally {
loading.value = false
}
}
const swapLanguages = () => {
// auto
if (sourceLang.value === 'auto') {
error.value = '无法交换:源语言为自动检测时不能进行语言交换'
return
}
const temp = sourceLang.value
sourceLang.value = targetLang.value
targetLang.value = temp
@ -203,6 +220,11 @@ const clearText = () => {
error.value = ''
}
//
const getLanguageDisplayName = (langCode: string): string => {
return langDisplayMap[langCode] || langCode
}
// localStorage
onMounted(() => {
const saved = localStorage.getItem('translationHistory')