feat: 完善 HomeView 国际化支持

- 新增 LanguageManager 支持应用内语言切换
- 新增 Localizable.xcstrings 包含 78 个翻译键
- 修复 HomeView 硬编码文本,改用 String(localized:)
- 支持简体中文、繁体中文、英文三种语言

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
empty
2026-01-03 22:19:02 +08:00
parent bf3f9d9eb2
commit 6d8a3a85a6
3 changed files with 1981 additions and 13 deletions

View File

@@ -0,0 +1,88 @@
import SwiftUI
///
@Observable
final class LanguageManager {
///
enum Language: String, CaseIterable, Identifiable {
case system = "system"
case zhHans = "zh-Hans"
case zhHant = "zh-Hant"
case en = "en"
var id: String { rawValue }
var displayName: String {
switch self {
case .system: return "跟随系统"
case .zhHans: return "简体中文"
case .zhHant: return "繁體中文"
case .en: return "English"
}
}
var locale: Locale? {
switch self {
case .system: return nil
case .zhHans: return Locale(identifier: "zh-Hans")
case .zhHant: return Locale(identifier: "zh-Hant")
case .en: return Locale(identifier: "en")
}
}
}
///
static let shared = LanguageManager()
///
var current: Language {
didSet {
UserDefaults.standard.set(current.rawValue, forKey: "app_language")
applyLanguage()
}
}
///
var availableLanguages: [Language] {
Language.allCases
}
private init() {
let savedLanguage = UserDefaults.standard.string(forKey: "app_language") ?? "system"
self.current = Language(rawValue: savedLanguage) ?? .system
applyLanguage()
}
///
private func applyLanguage() {
if current == .system {
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
} else {
UserDefaults.standard.set([current.rawValue], forKey: "AppleLanguages")
}
UserDefaults.standard.synchronize()
}
///
func localizedString(_ key: String) -> String {
if current == .system {
return String(localized: String.LocalizationValue(key))
}
guard let path = Bundle.main.path(forResource: current.rawValue, ofType: "lproj"),
let bundle = Bundle(path: path) else {
return String(localized: String.LocalizationValue(key))
}
return NSLocalizedString(key, bundle: bundle, comment: "")
}
}
// MARK: - 便
extension String {
///
var localized: String {
LanguageManager.shared.localizedString(self)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -71,11 +71,11 @@ struct HomeView: View {
}
VStack(spacing: DesignTokens.Spacing.sm) {
Text("Live Photo 制作")
Text(String(localized: "home.title"))
.font(.system(size: DesignTokens.FontSize.xxl, weight: .bold))
.foregroundColor(.textPrimary)
Text("选择视频,一键转换为动态壁纸")
Text(String(localized: "home.subtitle"))
.font(.system(size: DesignTokens.FontSize.base))
.foregroundColor(.textSecondary)
.multilineTextAlignment(.center)
@@ -90,7 +90,7 @@ struct HomeView: View {
HStack(spacing: DesignTokens.Spacing.sm) {
Image(systemName: "video.badge.plus")
.font(.system(size: 18, weight: .semibold))
Text("选择视频")
Text(String(localized: "home.selectVideo"))
.font(.system(size: DesignTokens.FontSize.base, weight: .semibold))
}
.foregroundColor(.white)
@@ -111,7 +111,7 @@ struct HomeView: View {
HStack(spacing: DesignTokens.Spacing.sm) {
ProgressView()
.tint(.accentPurple)
Text("正在加载视频...")
Text(String(localized: "home.loading"))
.font(.system(size: DesignTokens.FontSize.sm))
.foregroundColor(.textSecondary)
}
@@ -149,7 +149,7 @@ struct HomeView: View {
.foregroundColor(.accentOrange)
}
Text("快速上手")
Text(String(localized: "home.quickStart"))
.font(.system(size: DesignTokens.FontSize.lg, weight: .semibold))
.foregroundColor(.textPrimary)
@@ -157,15 +157,15 @@ struct HomeView: View {
}
VStack(alignment: .leading, spacing: DesignTokens.Spacing.md) {
QuickStartStep(number: 1, text: "点击上方「选择视频」导入素材", color: .accentPurple)
QuickStartStep(number: 2, text: "调整比例和时长,选择封面帧", color: .accentCyan)
QuickStartStep(number: 3, text: "开启 AI 增强提升画质(可选)", color: .accentPink)
QuickStartStep(number: 4, text: "生成后按引导设置为壁纸", color: .accentGreen)
QuickStartStep(number: 1, text: String(localized: "home.quickStart.step1"), color: .accentPurple)
QuickStartStep(number: 2, text: String(localized: "home.quickStart.step2"), color: .accentCyan)
QuickStartStep(number: 3, text: String(localized: "home.quickStart.step3"), color: .accentPink)
QuickStartStep(number: 4, text: String(localized: "home.quickStart.step4"), color: .accentGreen)
}
HStack {
Spacer()
Text("完成后的作品会显示在这里")
Text(String(localized: "home.emptyHint"))
.font(.system(size: DesignTokens.FontSize.xs))
.foregroundColor(.textMuted)
Spacer()
@@ -190,13 +190,13 @@ struct HomeView: View {
.foregroundColor(.accentCyan)
}
Text("最近作品")
Text(String(localized: "home.recentWorks"))
.font(.system(size: DesignTokens.FontSize.lg, weight: .semibold))
.foregroundColor(.textPrimary)
Spacer()
Text("\(recentWorks.recentWorks.count)")
Text(String(localized: "home.worksCount \(recentWorks.recentWorks.count)"))
.font(.system(size: DesignTokens.FontSize.sm))
.foregroundColor(.textMuted)
}
@@ -224,7 +224,7 @@ struct HomeView: View {
do {
guard let movie = try await item.loadTransferable(type: VideoTransferable.self) else {
errorMessage = "无法加载视频"
errorMessage = String(localized: "home.loadFailed")
isLoading = false
return
}