refactor: internationalize HomePage component

- Replace all hardcoded Chinese text with t() function calls
- Add Chinese translations for all new i18n keys in zh-CN.json
- Update UI text including buttons, labels, error messages, and status messages
- Ensure all user-facing strings are properly internationalized
This commit is contained in:
jingrow 2025-12-26 20:57:14 +08:00
parent 05fce42cbf
commit fd84f1458a
2 changed files with 83 additions and 59 deletions

View File

@ -1193,5 +1193,30 @@
"URL": "URL", "URL": "URL",
"Created": "创建时间", "Created": "创建时间",
"Last Updated": "最后更新", "Last Updated": "最后更新",
"Click image to try": "点击图片快速体验" "Click image to try": "点击图片快速体验",
"Remove Background": "去除背景",
"Download": "下载",
"Change Image": "更换图片",
"Upload Image": "上传图片",
"or": "或",
"Paste image URL here": "粘贴图片URL",
"Drag and drop your image anywhere, or paste image directly": "拖放图片到任意位置,或直接粘贴图片",
"Supports JPG, PNG, WebP formats": "支持 JPG、PNG、WebP 格式",
"Loading image from URL...": "加载图片URL中...",
"Original": "原图",
"Background Removed": "去背景后",
"Processing...": "处理中...",
"Add new image": "添加新图片",
"Delete": "删除",
"Unsupported image format. Please use JPG, PNG, or WebP": "不支持的图片格式,请使用 JPG、PNG 或 WebP",
"Image size exceeds 10MB limit": "图片大小超过 10MB 限制",
"Please upload an image first": "请先上传图片",
"Failed to download image": "下载失败",
"Please enter an image URL": "请输入图片URL",
"Please enter a valid image URL": "请输入有效的图片URL",
"Image processing failed, please try again": "图片处理失败,请重试",
"Failed to load sample image": "加载示例图片失败",
"Failed to remove background": "去背景失败",
"Failed to load image from URL": "从URL加载图片失败",
"Sign up successful, but auto login failed. Please login manually": "注册成功,但自动登录失败,请手动登录"
} }

View File

@ -160,7 +160,7 @@ const handleSignupSubmit = async () => {
signupFormData.email = '' signupFormData.email = ''
signupFormData.phoneNumber = '' signupFormData.phoneNumber = ''
} else { } else {
message.warning(loginResult.error || t('注册成功,但自动登录失败,请手动登录')) message.warning(loginResult.error || t('Sign up successful, but auto login failed. Please login manually'))
showSignupModal.value = false showSignupModal.value = false
showLoginModal.value = true showLoginModal.value = true
} }
@ -385,13 +385,13 @@ const handleFileSelect = (e: Event) => {
const processFile = async (file: File) => { const processFile = async (file: File) => {
const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'] const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
if (!validTypes.includes(file.type)) { if (!validTypes.includes(file.type)) {
message.warning('不支持的图片格式,请使用 JPG、PNG 或 WebP') message.warning(t('Unsupported image format. Please use JPG, PNG, or WebP'))
return return
} }
const maxSize = 10 * 1024 * 1024 const maxSize = 10 * 1024 * 1024
if (file.size > maxSize) { if (file.size > maxSize) {
message.warning('图片大小超过 10MB 限制') message.warning(t('Image size exceeds 10MB limit'))
return return
} }
@ -416,7 +416,7 @@ const processFile = async (file: File) => {
reader.readAsDataURL(compressedFile) reader.readAsDataURL(compressedFile)
} catch (error) { } catch (error) {
console.error('图片压缩失败:', error) console.error('图片压缩失败:', error)
message.error('图片处理失败,请重试') message.error(t('Image processing failed, please try again'))
} }
} }
@ -437,7 +437,7 @@ const handleSampleImageClick = async (imageUrl: string) => {
// //
const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'] const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
if (!validTypes.includes(blob.type)) { if (!validTypes.includes(blob.type)) {
message.warning('不支持的图片格式') message.warning(t('Unsupported image format. Please use JPG, PNG, or WebP'))
processing.value = false processing.value = false
return return
} }
@ -445,7 +445,7 @@ const handleSampleImageClick = async (imageUrl: string) => {
// //
const maxSize = 10 * 1024 * 1024 const maxSize = 10 * 1024 * 1024
if (blob.size > maxSize) { if (blob.size > maxSize) {
message.warning('图片大小超过 10MB 限制') message.warning(t('Image size exceeds 10MB limit'))
processing.value = false processing.value = false
return return
} }
@ -455,12 +455,12 @@ const handleSampleImageClick = async (imageUrl: string) => {
await processFile(file) await processFile(file)
} catch (error: any) { } catch (error: any) {
console.error('加载示例图片失败:', error) console.error('加载示例图片失败:', error)
let errorMessage = '加载示例图片失败' let errorMessage = t('Failed to load sample image')
if (error.message?.includes('CORS')) { if (error.message?.includes('CORS')) {
errorMessage = '图片服务器不允许跨域访问' errorMessage = t('CORS error. The image server does not allow cross-origin access.')
} else if (error.message?.includes('Failed to load')) { } else if (error.message?.includes('Failed to load')) {
errorMessage = '加载图片失败,请稍后重试' errorMessage = t('Failed to load image. Please check the URL and try again.')
} }
message.error(errorMessage) message.error(errorMessage)
@ -470,7 +470,7 @@ const handleSampleImageClick = async (imageUrl: string) => {
const handleRemoveBackground = async () => { const handleRemoveBackground = async () => {
if (!uploadedImage.value) { if (!uploadedImage.value) {
message.warning('请先上传图片') message.warning(t('Please upload an image first'))
return return
} }
@ -519,16 +519,16 @@ const handleRemoveBackground = async () => {
historyList.value[currentHistoryIndex.value].resultImage = firstResult.image_url historyList.value[currentHistoryIndex.value].resultImage = firstResult.image_url
} }
} else { } else {
message.error(firstResult.error || '去背景失败') message.error(firstResult.error || t('Failed to remove background'))
} }
} else { } else {
message.error(result.error || '去背景失败') message.error(result.error || t('Failed to remove background'))
} }
} else { } else {
message.error(response.data?.error || response.data?.message || '去背景失败') message.error(response.data?.error || response.data?.message || t('Failed to remove background'))
} }
} catch (error: any) { } catch (error: any) {
let errorMessage = '去背景失败' let errorMessage = t('Failed to remove background')
if (error.response?.data?.error) { if (error.response?.data?.error) {
errorMessage = error.response.data.error errorMessage = error.response.data.error
} else if (error.message) { } else if (error.message) {
@ -596,7 +596,7 @@ const handleDownload = async () => {
// blob URL使 // blob URL使
} catch (error) { } catch (error) {
console.error('下载失败:', error) console.error('下载失败:', error)
message.error('下载失败') message.error(t('Failed to download image'))
} }
} }
@ -789,12 +789,12 @@ const handleUrlPaste = async (event: ClipboardEvent) => {
const handleUrlSubmit = async () => { const handleUrlSubmit = async () => {
const url = imageUrl.value.trim() const url = imageUrl.value.trim()
if (!url) { if (!url) {
message.warning('请输入图片URL') message.warning(t('Please enter an image URL'))
return return
} }
if (!isValidImageUrl(url)) { if (!isValidImageUrl(url)) {
message.warning('请输入有效的图片URL') message.warning(t('Please enter a valid image URL'))
return return
} }
@ -813,14 +813,14 @@ const handleUrlSubmit = async () => {
const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'] const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']
if (!validTypes.includes(blob.type)) { if (!validTypes.includes(blob.type)) {
message.warning('不支持的图片格式,请使用 JPG、PNG 或 WebP') message.warning(t('Unsupported image format. Please use JPG, PNG, or WebP'))
processing.value = false processing.value = false
return return
} }
const maxSize = 10 * 1024 * 1024 const maxSize = 10 * 1024 * 1024
if (blob.size > maxSize) { if (blob.size > maxSize) {
message.warning('图片大小超过 10MB 限制') message.warning(t('Image size exceeds 10MB limit'))
processing.value = false processing.value = false
return return
} }
@ -842,17 +842,17 @@ const handleUrlSubmit = async () => {
await handleRemoveBackground() await handleRemoveBackground()
} catch (error) { } catch (error) {
console.error('图片压缩失败:', error) console.error('图片压缩失败:', error)
message.error('图片处理失败,请重试') message.error(t('Image processing failed, please try again'))
processing.value = false processing.value = false
} }
} catch (error: any) { } catch (error: any) {
console.error('加载图片URL失败:', error) console.error('加载图片URL失败:', error)
let errorMessage = '加载图片URL失败' let errorMessage = t('Failed to load image from URL')
if (error.message?.includes('CORS')) { if (error.message?.includes('CORS')) {
errorMessage = 'CORS错误。图片服务器不允许跨域访问。' errorMessage = t('CORS error. The image server does not allow cross-origin access.')
} else if (error.message?.includes('Failed to load')) { } else if (error.message?.includes('Failed to load')) {
errorMessage = '加载图片失败。请检查URL并重试。' errorMessage = t('Failed to load image. Please check the URL and try again.')
} }
message.error(errorMessage) message.error(errorMessage)
@ -955,8 +955,8 @@ onUnmounted(() => {
</div> </div>
<div class="header-right"> <div class="header-right">
<n-space :size="12"> <n-space :size="12">
<n-button quaternary @click="handleSignup">注册</n-button> <n-button quaternary @click="handleSignup">{{ t('Sign up') }}</n-button>
<n-button type="primary" @click="handleLogin" class="login-btn">登录</n-button> <n-button type="primary" @click="handleLogin" class="login-btn">{{ t('Login') }}</n-button>
</n-space> </n-space>
</div> </div>
</div> </div>
@ -974,12 +974,12 @@ onUnmounted(() => {
<div class="tool-page"> <div class="tool-page">
<div class="page-header"> <div class="page-header">
<h2>去除背景</h2> <h2>{{ t('Remove Background') }}</h2>
<div v-if="uploadedImage" class="toolbar-actions"> <div v-if="uploadedImage" class="toolbar-actions">
<button v-if="resultImage" class="toolbar-btn" @click="handleDownload" title="下载"> <button v-if="resultImage" class="toolbar-btn" @click="handleDownload" :title="t('Download')">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
</button> </button>
<button class="toolbar-btn" @click="resetUpload" title="更换图片"> <button class="toolbar-btn" @click="resetUpload" :title="t('Change Image')">
<i class="fa fa-refresh"></i> <i class="fa fa-refresh"></i>
</button> </button>
</div> </div>
@ -992,11 +992,11 @@ onUnmounted(() => {
<div class="upload-content"> <div class="upload-content">
<button type="button" class="upload-btn" @click="triggerFileInput" :disabled="processing"> <button type="button" class="upload-btn" @click="triggerFileInput" :disabled="processing">
<i class="fa fa-upload"></i> <i class="fa fa-upload"></i>
<span>上传图片</span> <span>{{ t('Upload Image') }}</span>
</button> </button>
<div class="divider"> <div class="divider">
<span></span> <span>{{ t('or') }}</span>
</div> </div>
<div class="url-input-wrapper" @click.stop> <div class="url-input-wrapper" @click.stop>
@ -1005,19 +1005,19 @@ onUnmounted(() => {
v-model="imageUrl" v-model="imageUrl"
type="text" type="text"
class="url-input" class="url-input"
placeholder="粘贴图片URL" :placeholder="t('Paste image URL here')"
@keyup.enter="handleUrlSubmit" @keyup.enter="handleUrlSubmit"
@paste="handleUrlPaste" @paste="handleUrlPaste"
:disabled="processing" :disabled="processing"
/> />
</div> </div>
<p class="upload-hint">拖放图片到任意位置或直接粘贴图片</p> <p class="upload-hint">{{ t('Drag and drop your image anywhere, or paste image directly') }}</p>
<p class="upload-format-hint">支持 JPGPNGWebP 格式</p> <p class="upload-format-hint">{{ t('Supports JPG, PNG, WebP formats') }}</p>
</div> </div>
<div v-if="processing" class="upload-processing-overlay"> <div v-if="processing" class="upload-processing-overlay">
<div class="spinner"></div> <div class="spinner"></div>
<p>加载图片URL中...</p> <p>{{ t('Loading image from URL...') }}</p>
</div> </div>
</div> </div>
@ -1025,10 +1025,10 @@ onUnmounted(() => {
<div class="comparison-view" v-if="resultImage"> <div class="comparison-view" v-if="resultImage">
<div class="comparison-container" ref="comparisonContainerRef"> <div class="comparison-container" ref="comparisonContainerRef">
<div class="comparison-image original-image" :style="{ clipPath: `inset(0 ${100 - splitPosition}% 0 0)` }"> <div class="comparison-image original-image" :style="{ clipPath: `inset(0 ${100 - splitPosition}% 0 0)` }">
<img ref="originalImageRef" :src="uploadedImageUrl" alt="原图" @load="adjustContainerSize" /> <img ref="originalImageRef" :src="uploadedImageUrl" :alt="t('Original')" @load="adjustContainerSize" />
</div> </div>
<div class="comparison-image result-image" :style="{ clipPath: `inset(0 0 0 ${splitPosition}%)` }"> <div class="comparison-image result-image" :style="{ clipPath: `inset(0 0 0 ${splitPosition}%)` }">
<img ref="resultImageRef" :src="resultImageUrl" alt="结果" @load="adjustContainerSize" /> <img ref="resultImageRef" :src="resultImageUrl" :alt="t('Background Removed')" @load="adjustContainerSize" />
</div> </div>
<div class="split-line" :class="{ dragging: isDraggingSplitLine }" :style="{ left: `${splitPosition}%` }" @mousedown.prevent.stop="handleSplitLineMouseDown"> <div class="split-line" :class="{ dragging: isDraggingSplitLine }" :style="{ left: `${splitPosition}%` }" @mousedown.prevent.stop="handleSplitLineMouseDown">
<div class="split-line-handle"> <div class="split-line-handle">
@ -1040,11 +1040,11 @@ onUnmounted(() => {
<div v-else class="single-image-view"> <div v-else class="single-image-view">
<div class="image-wrapper" ref="singleImageWrapperRef"> <div class="image-wrapper" ref="singleImageWrapperRef">
<div class="single-image-container"> <div class="single-image-container">
<img ref="singleImageRef" :src="uploadedImageUrl" alt="原图" @load="adjustContainerSize" /> <img ref="singleImageRef" :src="uploadedImageUrl" :alt="t('Original')" @load="adjustContainerSize" />
</div> </div>
<div v-if="processing" class="processing-overlay"> <div v-if="processing" class="processing-overlay">
<div class="spinner"></div> <div class="spinner"></div>
<p>处理中...</p> <p>{{ t('Processing...') }}</p>
</div> </div>
</div> </div>
</div> </div>
@ -1057,7 +1057,7 @@ onUnmounted(() => {
type="button" type="button"
class="history-item add-button" class="history-item add-button"
@click.stop="triggerFileInput" @click.stop="triggerFileInput"
title="添加新图片" :title="t('Add new image')"
> >
<i class="fa fa-plus"></i> <i class="fa fa-plus"></i>
</button> </button>
@ -1075,8 +1075,8 @@ onUnmounted(() => {
type="button" type="button"
class="history-delete-btn" class="history-delete-btn"
@click.stop="removeHistoryItem(index)" @click.stop="removeHistoryItem(index)"
title="删除" :title="t('Delete')"
:aria-label="'删除'" :aria-label="t('Delete')"
></button> ></button>
</div> </div>
</div> </div>
@ -1223,12 +1223,12 @@ onUnmounted(() => {
<div class="tool-page"> <div class="tool-page">
<div class="page-header"> <div class="page-header">
<h2>去除背景</h2> <h2>{{ t('Remove Background') }}</h2>
<div v-if="uploadedImage" class="toolbar-actions"> <div v-if="uploadedImage" class="toolbar-actions">
<button v-if="resultImage" class="toolbar-btn" @click="handleDownload" title="下载"> <button v-if="resultImage" class="toolbar-btn" @click="handleDownload" :title="t('Download')">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
</button> </button>
<button class="toolbar-btn" @click="resetUpload" title="更换图片"> <button class="toolbar-btn" @click="resetUpload" :title="t('Change Image')">
<i class="fa fa-refresh"></i> <i class="fa fa-refresh"></i>
</button> </button>
</div> </div>
@ -1241,11 +1241,11 @@ onUnmounted(() => {
<div class="upload-content"> <div class="upload-content">
<button type="button" class="upload-btn" @click="triggerFileInput" :disabled="processing"> <button type="button" class="upload-btn" @click="triggerFileInput" :disabled="processing">
<i class="fa fa-upload"></i> <i class="fa fa-upload"></i>
<span>上传图片</span> <span>{{ t('Upload Image') }}</span>
</button> </button>
<div class="divider"> <div class="divider">
<span></span> <span>{{ t('or') }}</span>
</div> </div>
<div class="url-input-wrapper" @click.stop> <div class="url-input-wrapper" @click.stop>
@ -1254,19 +1254,19 @@ onUnmounted(() => {
v-model="imageUrl" v-model="imageUrl"
type="text" type="text"
class="url-input" class="url-input"
placeholder="粘贴图片URL" :placeholder="t('Paste image URL here')"
@keyup.enter="handleUrlSubmit" @keyup.enter="handleUrlSubmit"
@paste="handleUrlPaste" @paste="handleUrlPaste"
:disabled="processing" :disabled="processing"
/> />
</div> </div>
<p class="upload-hint">拖放图片到任意位置或直接粘贴图片</p> <p class="upload-hint">{{ t('Drag and drop your image anywhere, or paste image directly') }}</p>
<p class="upload-format-hint">支持 JPGPNGWebP 格式</p> <p class="upload-format-hint">{{ t('Supports JPG, PNG, WebP formats') }}</p>
</div> </div>
<div v-if="processing" class="upload-processing-overlay"> <div v-if="processing" class="upload-processing-overlay">
<div class="spinner"></div> <div class="spinner"></div>
<p>加载图片URL中...</p> <p>{{ t('Loading image from URL...') }}</p>
</div> </div>
</div> </div>
@ -1274,10 +1274,10 @@ onUnmounted(() => {
<div class="comparison-view" v-if="resultImage"> <div class="comparison-view" v-if="resultImage">
<div class="comparison-container" ref="comparisonContainerRef"> <div class="comparison-container" ref="comparisonContainerRef">
<div class="comparison-image original-image" :style="{ clipPath: `inset(0 ${100 - splitPosition}% 0 0)` }"> <div class="comparison-image original-image" :style="{ clipPath: `inset(0 ${100 - splitPosition}% 0 0)` }">
<img ref="originalImageRef" :src="uploadedImageUrl" alt="原图" @load="adjustContainerSize" /> <img ref="originalImageRef" :src="uploadedImageUrl" :alt="t('Original')" @load="adjustContainerSize" />
</div> </div>
<div class="comparison-image result-image" :style="{ clipPath: `inset(0 0 0 ${splitPosition}%)` }"> <div class="comparison-image result-image" :style="{ clipPath: `inset(0 0 0 ${splitPosition}%)` }">
<img ref="resultImageRef" :src="resultImageUrl" alt="结果" @load="adjustContainerSize" /> <img ref="resultImageRef" :src="resultImageUrl" :alt="t('Background Removed')" @load="adjustContainerSize" />
</div> </div>
<div class="split-line" :class="{ dragging: isDraggingSplitLine }" :style="{ left: `${splitPosition}%` }" @mousedown.prevent.stop="handleSplitLineMouseDown"> <div class="split-line" :class="{ dragging: isDraggingSplitLine }" :style="{ left: `${splitPosition}%` }" @mousedown.prevent.stop="handleSplitLineMouseDown">
<div class="split-line-handle"> <div class="split-line-handle">
@ -1289,11 +1289,11 @@ onUnmounted(() => {
<div v-else class="single-image-view"> <div v-else class="single-image-view">
<div class="image-wrapper" ref="singleImageWrapperRef"> <div class="image-wrapper" ref="singleImageWrapperRef">
<div class="single-image-container"> <div class="single-image-container">
<img ref="singleImageRef" :src="uploadedImageUrl" alt="原图" @load="adjustContainerSize" /> <img ref="singleImageRef" :src="uploadedImageUrl" :alt="t('Original')" @load="adjustContainerSize" />
</div> </div>
<div v-if="processing" class="processing-overlay"> <div v-if="processing" class="processing-overlay">
<div class="spinner"></div> <div class="spinner"></div>
<p>处理中...</p> <p>{{ t('Processing...') }}</p>
</div> </div>
</div> </div>
</div> </div>
@ -1306,7 +1306,7 @@ onUnmounted(() => {
type="button" type="button"
class="history-item add-button" class="history-item add-button"
@click.stop="triggerFileInput" @click.stop="triggerFileInput"
title="添加新图片" :title="t('Add new image')"
> >
<i class="fa fa-plus"></i> <i class="fa fa-plus"></i>
</button> </button>
@ -1324,8 +1324,8 @@ onUnmounted(() => {
type="button" type="button"
class="history-delete-btn" class="history-delete-btn"
@click.stop="removeHistoryItem(index)" @click.stop="removeHistoryItem(index)"
title="删除" :title="t('Delete')"
:aria-label="'删除'" :aria-label="t('Delete')"
></button> ></button>
</div> </div>
</div> </div>
@ -1909,7 +1909,6 @@ onUnmounted(() => {
font-weight: 600; font-weight: 600;
color: #64748b; color: #64748b;
margin: 0 0 16px 0; margin: 0 0 16px 0;
text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
} }