db65db6232
Android UI Tests / ui-tests (push) Has been cancelled
Localize all user-facing strings across iOS (SwiftUI), shared Kotlin, and Android Compose into en/es/fr/de/pt/it/ja/ko/nl/zh: - iOS String Catalogs: main + widget Localizable.xcstrings, InfoPlist.xcstrings (permissions), plural variations, ~200 new keys translated - Shared Kotlin ClientStrings table + Android composeResources/values-* (884 keys ×10), routed Api/ViewModel/util error & UI strings through localization - Backend-localized lookups/suggestions consumed via display names - Widget extension catalog; theme names, home-profile fallbacks, validation, network errors, accessibility labels all localized Add re-runnable verification gates: - scripts/i18n_audit.py — enumerate every literal, partition to GAP=0 - scripts/i18n_coverage.py — all 10 locales translated, format-specifier parity Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
107 lines
3.5 KiB
Swift
107 lines
3.5 KiB
Swift
import SwiftUI
|
|
import ComposeApp
|
|
|
|
struct DocumentCard: View {
|
|
let document: Document
|
|
|
|
var typeColor: Color {
|
|
switch document.documentType {
|
|
case "warranty": return Color.appPrimary
|
|
case "manual": return Color.appSecondary
|
|
case "receipt": return Color.appAccent
|
|
case "inspection": return Color.appAccent
|
|
default: return Color.appTextSecondary
|
|
}
|
|
}
|
|
|
|
var typeIcon: String {
|
|
switch document.documentType {
|
|
case "photo": return "photo"
|
|
case "warranty", "insurance": return "checkmark.shield"
|
|
case "manual": return "book"
|
|
case "receipt": return "receipt"
|
|
default: return "doc.text"
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
HStack(spacing: AppSpacing.md) {
|
|
// Document Icon
|
|
VStack {
|
|
Image(systemName: typeIcon)
|
|
.font(.system(size: 24))
|
|
.foregroundColor(typeColor)
|
|
.background(content: {
|
|
RoundedRectangle(cornerRadius: 8)
|
|
.fill(typeColor.opacity(0.1))
|
|
.frame(width: 56, height: 56)
|
|
})
|
|
.padding(AppSpacing.md)
|
|
.accessibilityHidden(true)
|
|
|
|
Spacer()
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(document.title)
|
|
.font(.title3.weight(.semibold))
|
|
.fontWeight(.bold)
|
|
.foregroundColor(Color.appTextPrimary)
|
|
// .lineLimit(1)
|
|
|
|
if let description = document.description_, !description.isEmpty {
|
|
Text(description)
|
|
.font(.callout)
|
|
.foregroundColor(Color.appTextSecondary)
|
|
.lineLimit(2)
|
|
}
|
|
|
|
HStack(spacing: 8) {
|
|
Text(getDocTypeDisplayName(document.documentType))
|
|
.font(.caption.weight(.medium))
|
|
.foregroundColor(typeColor)
|
|
.padding(.horizontal, 6)
|
|
.padding(.vertical, 2)
|
|
.background(typeColor.opacity(0.2))
|
|
.cornerRadius(4)
|
|
|
|
if let fileSize = document.fileSize {
|
|
Text(formatFileSize(Int(fileSize)))
|
|
.font(.caption.weight(.medium))
|
|
.foregroundColor(Color.appTextSecondary)
|
|
}
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
Image(systemName: "chevron.right")
|
|
.foregroundColor(Color.appTextSecondary)
|
|
.font(.system(size: 14))
|
|
.accessibilityHidden(true)
|
|
}
|
|
.padding(AppSpacing.md)
|
|
.background(Color.appBackgroundSecondary)
|
|
.cornerRadius(AppRadius.md)
|
|
.shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1)
|
|
.accessibilityElement(children: .combine)
|
|
}
|
|
|
|
private func getDocTypeDisplayName(_ type: String) -> String {
|
|
return DocumentTypeHelper.displayName(for: type)
|
|
}
|
|
|
|
private func formatFileSize(_ bytes: Int) -> String {
|
|
var size = Double(bytes)
|
|
let units = ["B", "KB", "MB", "GB"]
|
|
var unitIndex = 0
|
|
|
|
while size >= 1024 && unitIndex < units.count - 1 {
|
|
size /= 1024
|
|
unitIndex += 1
|
|
}
|
|
|
|
return String(format: "%.1f %@", size, units[unitIndex])
|
|
}
|
|
}
|