feat: initialize AeroStart browser start page project

Implement a modern, customizable browser start page with comprehensive features:

- Multi-theme support with 8 preset color schemes
- Custom wallpaper system supporting images and videos with multiple fit modes
- Integrated search functionality with 5 major search engines (Google, Baidu, Bing, DuckDuckGo, Bilibili)
- Real-time clock component with 12/24 hour format options
- Dynamic background blur effect during search for enhanced focus
- Complete i18n system with English and Chinese language support
- Responsive design with smooth animations and transitions
- Local storage integration for persistent user preferences
- Context menu system for quick settings access
- Toast notification system for user feedback
- Error boundary for robust error handling

Tech Stack:
- React 19 with TypeScript
- Vite 6 for build tooling
- Tailwind CSS for styling
- Local storage for data persistence

Project Structure:
- Core components: Clock, SearchBox, SettingsModal, ThemeSettings, WallpaperManager
- Utility modules: storage management, search suggestions
- Context providers: Toast notifications, i18n
- Type definitions and constants configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZyphrZero
2025-12-05 02:55:50 +08:00
parent aa197e4e48
commit 56dd6d8bf2
31 changed files with 5270 additions and 0 deletions

45
i18n/I18nContext.tsx Normal file
View File

@@ -0,0 +1,45 @@
import React, { createContext, useContext, ReactNode } from 'react';
import { Language, Translation } from './types';
import { en } from './locales/en';
import { zh } from './locales/zh';
interface I18nContextType {
language: Language;
t: Translation;
setLanguage: (lang: Language) => void;
}
const I18nContext = createContext<I18nContextType | undefined>(undefined);
const translations: Record<Language, Translation> = {
en,
zh,
};
interface I18nProviderProps {
children: ReactNode;
language: Language;
onLanguageChange: (lang: Language) => void;
}
export const I18nProvider: React.FC<I18nProviderProps> = ({
children,
language,
onLanguageChange
}) => {
const t = translations[language];
return (
<I18nContext.Provider value={{ language, t, setLanguage: onLanguageChange }}>
{children}
</I18nContext.Provider>
);
};
export const useTranslation = () => {
const context = useContext(I18nContext);
if (!context) {
throw new Error('useTranslation must be used within an I18nProvider');
}
return context;
};

228
i18n/README.md Normal file
View File

@@ -0,0 +1,228 @@
# 多语言架构使用指南 (i18n Architecture Guide)
## 📁 项目结构
```
i18n/
├── types.ts # 类型定义
├── locales/
│ ├── en.ts # 英文翻译
│ └── zh.ts # 中文翻译
├── I18nContext.tsx # Context 和 Provider
├── index.ts # 导出文件
└── README.md # 本文档
```
## 🚀 快速开始
### 1. 在组件中使用翻译
```tsx
import { useTranslation } from '../i18n';
const MyComponent = () => {
const { t, language, setLanguage } = useTranslation();
return (
<div>
<h1>{t.settings}</h1>
<p>{t.appearance}</p>
<button onClick={() => setLanguage('zh')}>切换到中文</button>
</div>
);
};
```
### 2. 添加新的翻译键
#### 步骤 1: 在 `types.ts` 中添加类型定义
```typescript
export interface Translation {
// ... 现有的键
myNewKey: string;
}
```
#### 步骤 2: 在 `locales/en.ts` 中添加英文翻译
```typescript
export const en: Translation = {
// ... 现有的翻译
myNewKey: 'My New Text',
};
```
#### 步骤 3: 在 `locales/zh.ts` 中添加中文翻译
```typescript
export const zh: Translation = {
// ... 现有的翻译
myNewKey: '我的新文本',
};
```
### 3. 在组件中使用新的翻译键
```tsx
const MyComponent = () => {
const { t } = useTranslation();
return <div>{t.myNewKey}</div>;
};
```
## 📝 已支持的语言
- **English (en)** - 英语
- **简体中文 (zh)** - Simplified Chinese
## 🎯 已翻译的组件
以下组件已经集成了多语言支持:
-**ThemeSettings** - 主题设置(包含语言切换器)
-**SettingsModal** - 设置模态框(待更新)
-**SearchBox** - 搜索框(待更新)
-**SearchEngineManager** - 搜索引擎管理器(待更新)
-**WallpaperManager** - 壁纸管理器(待更新)
-**GlobalContextMenu** - 全局右键菜单(待更新)
-**ErrorBoundary** - 错误边界(待更新)
-**Clock** - 时钟(待更新)
## 🔧 API 参考
### `useTranslation()` Hook
返回一个包含以下属性的对象:
- **`t`**: `Translation` - 当前语言的翻译对象
- **`language`**: `Language` - 当前语言 ('en' | 'zh')
- **`setLanguage`**: `(lang: Language) => void` - 切换语言的函数
### `I18nProvider` Component
Props:
- **`language`**: `Language` - 当前语言
- **`onLanguageChange`**: `(lang: Language) => void` - 语言变化回调
- **`children`**: `ReactNode` - 子组件
## 💡 最佳实践
### 1. 保持翻译键的一致性
使用描述性的键名,例如:
-`searchEngineDeleted`
-`msg1`
### 2. 避免在翻译中使用 HTML
如果需要格式化文本,使用多个翻译键:
```tsx
// ❌ 不推荐
myKey: '<strong>Bold</strong> text'
// ✅ 推荐
myKeyBold: 'Bold'
myKeyText: 'text'
// 在组件中使用
<div><strong>{t.myKeyBold}</strong> {t.myKeyText}</div>
```
### 3. 为长文本使用描述性键名
```typescript
// ✅ 好的命名
errorMessage: 'The application encountered an unexpected error...'
// ❌ 不好的命名
error: 'The application encountered an unexpected error...'
```
### 4. 组织相关的翻译键
`Translation` 接口中使用注释分组:
```typescript
export interface Translation {
// Common
settings: string;
appearance: string;
// Theme Settings
themeColor: string;
showSeconds: string;
// Error Messages
errorMessage: string;
somethingWentWrong: string;
}
```
## 🌍 添加新语言
### 步骤 1: 在 `types.ts` 中添加语言类型
```typescript
export type Language = 'en' | 'zh' | 'ja'; // 添加 'ja' 日语
```
### 步骤 2: 创建新的语言文件
创建 `locales/ja.ts`:
```typescript
import { Translation } from '../types';
export const ja: Translation = {
settings: '設定',
appearance: '外観',
// ... 其他翻译
};
```
### 步骤 3: 在 `I18nContext.tsx` 中注册新语言
```typescript
const translations: Record<Language, Translation> = {
en,
zh,
ja, // 添加日语
};
```
### 步骤 4: 在 `ThemeSettings` 中添加语言选项
```tsx
<button onClick={() => handleLanguageChange('ja')}>
日本語
</button>
```
## 🐛 故障排除
### 问题:翻译不显示
**解决方案:**
1. 确保组件在 `I18nProvider` 内部
2. 检查是否正确导入了 `useTranslation`
3. 验证翻译键在所有语言文件中都存在
### 问题TypeScript 错误
**解决方案:**
1. 确保所有语言文件都实现了 `Translation` 接口
2. 运行 `npx tsc --noEmit` 检查类型错误
3. 确保新添加的键在 `types.ts` 中有定义
## 📚 相关文件
- `types.ts` - UserSettings 类型(包含 language 字段)
- `App.tsx` - I18nProvider 集成
- `utils/storage.ts` - 语言偏好持久化
## 🎉 完成!
现在你已经了解了如何使用和扩展多语言架构。如果有任何问题,请参考现有组件的实现或查看本文档。

4
i18n/index.ts Normal file
View File

@@ -0,0 +1,4 @@
export * from './types';
export * from './I18nContext';
export { en } from './locales/en';
export { zh } from './locales/zh';

95
i18n/locales/en.ts Normal file
View File

@@ -0,0 +1,95 @@
import { Translation } from '../types';
export const en: Translation = {
// Common
settings: 'Settings',
appearance: 'Appearance',
searchEngines: 'Search Engines',
// Theme Settings
themeColor: 'Theme Color',
showSeconds: 'Show Seconds',
use24HourFormat: '24-Hour Format',
maskBlurEffect: 'Mask Blur Effect',
searchHistory: 'Search History',
backgroundBlur: 'Background Blur',
searchBoxOpacity: 'Search Box Opacity',
// Wallpaper Settings
wallpaperSettings: 'Wallpaper Settings',
uploadImageVideo: 'Upload Image/Video',
enterImageVideoUrl: 'Enter image or video URL...',
apply: 'Apply',
cover: 'Cover',
contain: 'Contain',
fill: 'Fill',
repeat: 'Repeat',
center: 'Center',
deleteWallpaper: 'Delete wallpaper',
// Search Engine Manager
addCustomEngine: 'Add Custom Engine',
editSearchEngine: 'Edit Search Engine',
name: 'Name',
searchUrl: 'Search URL (use %s or append directly)',
svgIconCode: 'SVG Icon Code',
optional: 'optional',
preview: 'Preview',
cancel: 'Cancel',
save: 'Save',
add: 'Add',
current: 'Current',
setDefault: 'Set Default',
edit: 'Edit',
delete: 'Delete',
// Search Box
search: 'Search',
searchOn: 'Search on',
recentSearches: 'Recent Searches',
clearHistory: 'Clear History',
// Context Menu
copy: 'Copy',
cut: 'Cut',
paste: 'Paste',
// Error Boundary
somethingWentWrong: 'Something went wrong',
errorMessage: 'The application encountered an unexpected error. Please try refreshing the page or resetting the app.',
retry: 'Retry',
refreshPage: 'Refresh Page',
// Toast Messages
searchEngineDeleted: 'Search engine deleted',
searchEngineUpdated: 'Search engine updated successfully',
newSearchEngineAdded: 'New search engine added',
duplicateEngineName: 'This search engine name already exists, please use a different name',
customWallpaperApplied: 'Custom wallpaper applied',
wallpaperUploaded: 'Wallpaper uploaded and applied successfully',
wallpaperDeleted: 'Custom wallpaper deleted',
fileSizeExceeded: 'File size cannot exceed 3.5MB. Consider using URL method instead.',
unsupportedFileType: 'Unsupported file type. Only supports: JPEG, PNG, GIF, WebP, SVG, MP4, WebM, OGG',
fileContentMismatch: 'File content does not match type',
storageFull: 'Insufficient storage space! File too large to save. Consider using URL method.',
invalidUrlFormat: 'Invalid URL format',
unsupportedProtocol: 'Only HTTP or HTTPS protocol links are supported',
invalidSearchUrl: 'Generated search URL is invalid',
copyFailed: 'Copy failed',
cutFailed: 'Cut failed',
cannotReadClipboard: 'Cannot read clipboard',
// Clock
monday: 'Monday',
tuesday: 'Tuesday',
wednesday: 'Wednesday',
thursday: 'Thursday',
friday: 'Friday',
saturday: 'Saturday',
sunday: 'Sunday',
// Language
language: 'Language',
english: 'English',
chinese: '简体中文',
};

95
i18n/locales/zh.ts Normal file
View File

@@ -0,0 +1,95 @@
import { Translation } from '../types';
export const zh: Translation = {
// Common
settings: '设置',
appearance: '外观',
searchEngines: '搜索引擎',
// Theme Settings
themeColor: '主题颜色',
showSeconds: '显示秒数',
use24HourFormat: '24小时制',
maskBlurEffect: '遮罩层毛玻璃',
searchHistory: '搜索历史记录',
backgroundBlur: '背景模糊度',
searchBoxOpacity: '搜索框不透明度',
// Wallpaper Settings
wallpaperSettings: '壁纸设置',
uploadImageVideo: '上传图片/视频',
enterImageVideoUrl: '输入图片或视频链接...',
apply: '应用',
cover: '填充',
contain: '适应',
fill: '拉伸',
repeat: '平铺',
center: '居中',
deleteWallpaper: '删除壁纸',
// Search Engine Manager
addCustomEngine: '添加自定义引擎',
editSearchEngine: '编辑搜索引擎',
name: '名称',
searchUrl: '搜索 URL (使用 %s 或直接结尾)',
svgIconCode: 'SVG 图标代码',
optional: '可选',
preview: '预览',
cancel: '取消',
save: '保存',
add: '添加',
current: '当前使用',
setDefault: '设为默认',
edit: '编辑',
delete: '删除',
// Search Box
search: 'Search',
searchOn: 'Search on',
recentSearches: '最近搜索',
clearHistory: '清空历史记录',
// Context Menu
copy: '复制',
cut: '剪切',
paste: '粘贴',
// Error Boundary
somethingWentWrong: '出错了',
errorMessage: '应用遇到了一个意外错误。请尝试刷新页面或重置应用。',
retry: '重试',
refreshPage: '刷新页面',
// Toast Messages
searchEngineDeleted: '搜索引擎已删除',
searchEngineUpdated: '搜索引擎更新成功',
newSearchEngineAdded: '新搜索引擎已添加',
duplicateEngineName: '该搜索引擎名称已存在,请使用其他名称',
customWallpaperApplied: '自定义壁纸已应用',
wallpaperUploaded: '壁纸上传并应用成功',
wallpaperDeleted: '自定义壁纸已删除',
fileSizeExceeded: '文件大小不能超过 3.5MB。建议使用URL方式添加。',
unsupportedFileType: '不支持的文件类型。仅支持JPEG, PNG, GIF, WebP, SVG, MP4, WebM, OGG',
fileContentMismatch: '文件内容与类型不匹配',
storageFull: '存储空间不足!文件过大,无法保存。建议使用 URL 方式。',
invalidUrlFormat: '无效的 URL 格式',
unsupportedProtocol: '仅支持 HTTP 或 HTTPS 协议的链接',
invalidSearchUrl: '生成的搜索 URL 无效',
copyFailed: '复制失败',
cutFailed: '剪切失败',
cannotReadClipboard: '无法读取剪贴板',
// Clock
monday: '星期一',
tuesday: '星期二',
wednesday: '星期三',
thursday: '星期四',
friday: '星期五',
saturday: '星期六',
sunday: '星期日',
// Language
language: '语言',
english: 'English',
chinese: '简体中文',
};

95
i18n/types.ts Normal file
View File

@@ -0,0 +1,95 @@
export type Language = 'en' | 'zh';
export interface Translation {
// Common
settings: string;
appearance: string;
searchEngines: string;
// Theme Settings
themeColor: string;
showSeconds: string;
use24HourFormat: string;
maskBlurEffect: string;
searchHistory: string;
backgroundBlur: string;
searchBoxOpacity: string;
// Wallpaper Settings
wallpaperSettings: string;
uploadImageVideo: string;
enterImageVideoUrl: string;
apply: string;
cover: string;
contain: string;
fill: string;
repeat: string;
center: string;
deleteWallpaper: string;
// Search Engine Manager
addCustomEngine: string;
editSearchEngine: string;
name: string;
searchUrl: string;
svgIconCode: string;
optional: string;
preview: string;
cancel: string;
save: string;
add: string;
current: string;
setDefault: string;
edit: string;
delete: string;
// Search Box
search: string;
searchOn: string;
recentSearches: string;
clearHistory: string;
// Context Menu
copy: string;
cut: string;
paste: string;
// Error Boundary
somethingWentWrong: string;
errorMessage: string;
retry: string;
refreshPage: string;
// Toast Messages
searchEngineDeleted: string;
searchEngineUpdated: string;
newSearchEngineAdded: string;
duplicateEngineName: string;
customWallpaperApplied: string;
wallpaperUploaded: string;
wallpaperDeleted: string;
fileSizeExceeded: string;
unsupportedFileType: string;
fileContentMismatch: string;
storageFull: string;
invalidUrlFormat: string;
unsupportedProtocol: string;
invalidSearchUrl: string;
copyFailed: string;
cutFailed: string;
cannotReadClipboard: string;
// Clock
monday: string;
tuesday: string;
wednesday: string;
thursday: string;
friday: string;
saturday: string;
sunday: string;
// Language
language: string;
english: string;
chinese: string;
}