diff --git a/src/renderer/src/windows/migrationV2/MigrationApp.tsx b/src/renderer/src/windows/migrationV2/MigrationApp.tsx index 38aef8145d..8a547a3322 100644 --- a/src/renderer/src/windows/migrationV2/MigrationApp.tsx +++ b/src/renderer/src/windows/migrationV2/MigrationApp.tsx @@ -1,9 +1,10 @@ -import { Button } from '@cherrystudio/ui' +import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@cherrystudio/ui' import { AppLogo } from '@renderer/config/env' import { loggerService } from '@renderer/services/LoggerService' import { Progress, Space, Steps } from 'antd' import { AlertTriangle, CheckCircle, CheckCircle2, Database, Loader2, Rocket } from 'lucide-react' import React, { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import styled from 'styled-components' import { MigratorProgressList } from './components' @@ -14,10 +15,15 @@ import { MigrationIpcChannels } from './types' const logger = loggerService.withContext('MigrationApp') const MigrationApp: React.FC = () => { + const { t, i18n } = useTranslation() const { progress, lastError, confirmComplete } = useMigrationProgress() const actions = useMigrationActions() const [isLoading, setIsLoading] = useState(false) + const handleLanguageChange = (lang: string) => { + i18n.changeLanguage(lang) + } + const handleStartMigration = async () => { setIsLoading(true) try { @@ -118,19 +124,19 @@ const MigrationApp: React.FC = () => { case 'introduction': return ( <> - + - + ) case 'backup_required': return ( <> - + - - + + ) @@ -139,17 +145,17 @@ const MigrationApp: React.FC = () => {
) case 'backup_confirmed': return ( - + @@ -158,29 +164,29 @@ const MigrationApp: React.FC = () => { return (
- +
) case 'migration_completed': return (
- +
) case 'completed': return (
- +
) case 'error': return ( - + - + ) @@ -193,7 +199,7 @@ const MigrationApp: React.FC = () => {
- 数据迁移向导 + {t('migration.title')}
@@ -204,9 +210,25 @@ const MigrationApp: React.FC = () => { current={currentStep} status={stepStatus} size="small" - items={[{ title: '介绍' }, { title: '备份' }, { title: '迁移' }, { title: '完成' }]} + items={[ + { title: t('migration.stages.introduction') }, + { title: t('migration.stages.backup') }, + { title: t('migration.stages.migration') }, + { title: t('migration.stages.completed') } + ]} /> + + + @@ -215,46 +237,44 @@ const MigrationApp: React.FC = () => { {progress.stage === 'introduction' && ( - 将数据迁移到新的架构中 + {t('migration.introduction.title')} - Cherry Studio对数据的存储和使用方式进行了重大重构,在新的架构下,效率和安全性将会得到极大提升。 + {t('migration.introduction.description_1')}

- 数据必须进行迁移,才能在新版本中使用。 + {t('migration.introduction.description_2')}

- 我们会指导你完成迁移,迁移过程不会损坏原来的数据,你随时可以取消迁移,并继续使用旧版本。 + {t('migration.introduction.description_3')}
)} {progress.stage === 'backup_required' && ( - 创建数据备份 - - 迁移前必须创建数据备份以确保数据安全。请选择备份位置或确认已有最新备份。 - + {t('migration.backup_required.title')} + {t('migration.backup_required.description')} )} {progress.stage === 'backup_progress' && ( - 准备数据备份 - 请选择备份位置,保存后等待备份完成。 + {t('migration.backup_progress.title')} + {t('migration.backup_progress.description')} )} {progress.stage === 'backup_confirmed' && ( - 备份完成 - 数据备份已完成,现在可以安全地开始迁移。 + {t('migration.backup_confirmed.title')} + {t('migration.backup_confirmed.description')} )} {progress.stage === 'migration' && (
- 正在迁移数据... + {t('migration.migration.title')} {progress.currentMessage} @@ -273,8 +293,8 @@ const MigrationApp: React.FC = () => { {progress.stage === 'migration_completed' && (
- 数据迁移完成! - 所有数据已成功迁移到新架构,请点击确定继续。 + {t('migration.migration_completed.title')} + {t('migration.migration_completed.description')} @@ -287,19 +307,20 @@ const MigrationApp: React.FC = () => { {progress.stage === 'completed' && ( - 迁移完成 - 数据已成功迁移,重启应用后即可正常使用。 + {t('migration.completed.title')} + {t('migration.completed.description')} )} {progress.stage === 'error' && ( - 迁移失败 + {t('migration.error.title')} - 迁移过程遇到错误,您可以重新尝试或继续使用之前版本(原始数据完好保存)。 + {t('migration.error.description')}

- 错误信息:{lastError || progress.error || '发生未知错误'} + {t('migration.error.error_prefix')} + {lastError || progress.error || 'Unknown error'}
)} @@ -385,6 +406,11 @@ const StepsContainer = styled.div` } ` +const LanguageSelectorContainer = styled.div` + padding: 16px 24px 24px 24px; + border-top: 1px solid #f0f0f0; +` + const RightContent = styled.div` flex: 1; display: flex; diff --git a/src/renderer/src/windows/migrationV2/components/MigratorProgress.tsx b/src/renderer/src/windows/migrationV2/components/MigratorProgress.tsx index 428e46cc5a..65b7a40d82 100644 --- a/src/renderer/src/windows/migrationV2/components/MigratorProgress.tsx +++ b/src/renderer/src/windows/migrationV2/components/MigratorProgress.tsx @@ -5,6 +5,7 @@ import { CheckCircle2, Circle, Loader2, XCircle } from 'lucide-react' import React from 'react' +import { useTranslation } from 'react-i18next' import styled, { keyframes } from 'styled-components' import type { MigratorProgress as MigratorProgressType, MigratorStatus } from '../types' @@ -41,14 +42,13 @@ const SpinningIcon = styled.div` animation: ${spin} 1s linear infinite; ` -const statusTextMap: Record = { - pending: '等待中', - running: '进行中', - completed: '完成', - failed: '失败' -} - export const MigratorProgressList: React.FC = ({ migrators }) => { + const { t } = useTranslation() + + const getStatusText = (status: MigratorStatus): string => { + return t(`migration.status.${status}`) + } + return ( @@ -58,7 +58,7 @@ export const MigratorProgressList: React.FC = ({ migrators }) => { {migrator.name} - {migrator.error || statusTextMap[migrator.status]} + {migrator.error || getStatusText(migrator.status)} ))} diff --git a/src/renderer/src/windows/migrationV2/entryPoint.tsx b/src/renderer/src/windows/migrationV2/entryPoint.tsx index 0297369a21..53c0ec282c 100644 --- a/src/renderer/src/windows/migrationV2/entryPoint.tsx +++ b/src/renderer/src/windows/migrationV2/entryPoint.tsx @@ -9,6 +9,7 @@ import '@ant-design/v5-patch-for-react-19' import { loggerService } from '@logger' import { createRoot } from 'react-dom/client' +import { initI18n } from './i18n' import MigrationApp from './MigrationApp' // Initialize logger for this window @@ -16,4 +17,7 @@ loggerService.initWindowSource('MigrationV2') const root = createRoot(document.getElementById('root') as HTMLElement) -root.render() +// Wait for i18n to be fully initialized before rendering +initI18n().then(() => { + root.render() +}) diff --git a/src/renderer/src/windows/migrationV2/i18n/index.ts b/src/renderer/src/windows/migrationV2/i18n/index.ts new file mode 100644 index 0000000000..5f967ebf80 --- /dev/null +++ b/src/renderer/src/windows/migrationV2/i18n/index.ts @@ -0,0 +1,43 @@ +/** + * i18n initialization for migration window + * Detects system language independently without relying on preferenceService + */ + +import i18n from 'i18next' +import { initReactI18next } from 'react-i18next' + +import { enUS, zhCN } from './locales' + +/** + * Detect system language independently + * Rule: If system language contains 'zh', use Chinese, otherwise use English + */ +function detectLanguage(): 'zh-CN' | 'en-US' { + const browserLang = navigator.language || navigator.languages?.[0] || 'en-US' + + // If contains 'zh' (zh, zh-CN, zh-TW, zh-HK, etc.), use Chinese + return browserLang.toLowerCase().includes('zh') ? 'zh-CN' : 'en-US' +} + +const language = detectLanguage() + +/** + * Initialize i18n asynchronously + * Must be called and awaited before rendering components + */ +const initI18n = async () => { + await i18n.use(initReactI18next).init({ + resources: { + 'zh-CN': { translation: zhCN }, + 'en-US': { translation: enUS } + }, + lng: language, + fallbackLng: 'en-US', + interpolation: { + escapeValue: false + } + }) +} + +export default i18n +export { initI18n } diff --git a/src/renderer/src/windows/migrationV2/i18n/locales.ts b/src/renderer/src/windows/migrationV2/i18n/locales.ts new file mode 100644 index 0000000000..99e32d5fa6 --- /dev/null +++ b/src/renderer/src/windows/migrationV2/i18n/locales.ts @@ -0,0 +1,139 @@ +/** + * Migration window translations + * Supports Chinese (zh-CN) and English (en-US) + */ + +export const zhCN = { + migration: { + title: '数据迁移向导', + stages: { + introduction: '介绍', + backup: '备份', + migration: '迁移', + completed: '完成' + }, + buttons: { + cancel: '取消', + next: '下一步', + create_backup: '创建备份', + confirm_backup: '我已备份,开始迁移', + start_migration: '开始迁移', + confirm: '确定', + restart: '重启应用', + retry: '重新尝试', + close: '关闭应用', + backing_up: '正在备份...', + migrating: '迁移进行中...' + }, + status: { + pending: '等待中', + running: '进行中', + completed: '完成', + failed: '失败' + }, + introduction: { + title: '将数据迁移到新的架构中', + description_1: + 'Cherry Studio对数据的存储和使用方式进行了重大重构,在新的架构下,效率和安全性将会得到极大提升。', + description_2: '数据必须进行迁移,才能在新版本中使用。', + description_3: '我们会指导你完成迁移,迁移过程不会损坏原来的数据,你随时可以取消迁移,并继续使用旧版本。' + }, + backup_required: { + title: '创建数据备份', + description: '迁移前必须创建数据备份以确保数据安全。请选择备份位置或确认已有最新备份。' + }, + backup_progress: { + title: '准备数据备份', + description: '请选择备份位置,保存后等待备份完成。' + }, + backup_confirmed: { + title: '备份完成', + description: '数据备份已完成,现在可以安全地开始迁移。' + }, + migration: { + title: '正在迁移数据...' + }, + migration_completed: { + title: '数据迁移完成!', + description: '所有数据已成功迁移到新架构,请点击确定继续。' + }, + completed: { + title: '迁移完成', + description: '数据已成功迁移,重启应用后即可正常使用。' + }, + error: { + title: '迁移失败', + description: '迁移过程遇到错误,您可以重新尝试或继续使用之前版本(原始数据完好保存)。', + error_prefix: '错误信息:' + } + } +} + +export const enUS = { + migration: { + title: 'Data Migration Wizard', + stages: { + introduction: 'Introduction', + backup: 'Backup', + migration: 'Migration', + completed: 'Completed' + }, + buttons: { + cancel: 'Cancel', + next: 'Next', + create_backup: 'Create Backup', + confirm_backup: 'I Have Backup, Start Migration', + start_migration: 'Start Migration', + confirm: 'OK', + restart: 'Restart App', + retry: 'Retry', + close: 'Close App', + backing_up: 'Backing up...', + migrating: 'Migrating...' + }, + status: { + pending: 'Pending', + running: 'Running', + completed: 'Completed', + failed: 'Failed' + }, + introduction: { + title: 'Migrate Data to New Architecture', + description_1: + 'Cherry Studio has undergone a major refactoring of data storage and usage. The new architecture will greatly improve efficiency and security.', + description_2: 'Data migration is required to use the new version.', + description_3: + 'We will guide you through the migration process. The migration will not damage your original data, and you can cancel at any time and continue using the old version.' + }, + backup_required: { + title: 'Create Data Backup', + description: + 'A data backup must be created before migration to ensure data safety. Please select a backup location or confirm you have a recent backup.' + }, + backup_progress: { + title: 'Preparing Data Backup', + description: 'Please select a backup location, save, and wait for the backup to complete.' + }, + backup_confirmed: { + title: 'Backup Completed', + description: 'Data backup has been completed. You can now safely start the migration.' + }, + migration: { + title: 'Migrating Data...' + }, + migration_completed: { + title: 'Data Migration Completed!', + description: 'All data has been successfully migrated to the new architecture. Please click OK to continue.' + }, + completed: { + title: 'Migration Completed', + description: 'Data has been successfully migrated. The application will work normally after restart.' + }, + error: { + title: 'Migration Failed', + description: + 'An error occurred during migration. You can retry or continue using the previous version (original data is intact).', + error_prefix: 'Error: ' + } + } +}