Add comprehensive i18n localization for KMM and iOS
KMM (Android/Shared): - Add strings.xml with 200+ localized strings - Add translation files for es, fr, de, pt languages - Update all screens to use stringResource() for i18n - Add Accept-Language header to API client for all platforms iOS: - Add L10n.swift helper with type-safe string accessors - Add Localizable.xcstrings with translations for all 5 languages - Update all SwiftUI views to use L10n.* for localized strings - Localize Auth, Residence, Task, Contractor, Document, and Profile views Supported languages: English, Spanish, French, German, Portuguese 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -54,35 +54,35 @@ struct ResidenceDetailView: View {
|
||||
leadingToolbar
|
||||
trailingToolbar
|
||||
}
|
||||
|
||||
|
||||
// MARK: Alerts
|
||||
.alert("Generate Report", isPresented: $showReportConfirmation) {
|
||||
Button("Cancel", role: .cancel) {
|
||||
.alert(L10n.Residences.generateReport, isPresented: $showReportConfirmation) {
|
||||
Button(L10n.Common.cancel, role: .cancel) {
|
||||
showReportConfirmation = false
|
||||
}
|
||||
Button("Generate") {
|
||||
Button(L10n.Residences.generate) {
|
||||
viewModel.generateTasksReport(residenceId: residenceId, email: "")
|
||||
showReportConfirmation = false
|
||||
}
|
||||
} message: {
|
||||
Text("This will generate a comprehensive report of your property including all tasks, documents, and contractors.")
|
||||
Text(L10n.Residences.generateReportMessage)
|
||||
}
|
||||
|
||||
.alert("Delete Residence", isPresented: $showDeleteConfirmation) {
|
||||
Button("Cancel", role: .cancel) { }
|
||||
|
||||
.alert(L10n.Residences.deleteTitle, isPresented: $showDeleteConfirmation) {
|
||||
Button(L10n.Common.cancel, role: .cancel) { }
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Alert.cancelButton)
|
||||
Button("Delete", role: .destructive) {
|
||||
Button(L10n.Common.delete, role: .destructive) {
|
||||
deleteResidence()
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Alert.deleteButton)
|
||||
} message: {
|
||||
if let residence = viewModel.selectedResidence {
|
||||
Text("Are you sure you want to delete \(residence.name)? This action cannot be undone and will delete all associated tasks, documents, and data.")
|
||||
Text("\(L10n.Residences.deleteConfirmMessage)")
|
||||
}
|
||||
}
|
||||
|
||||
.alert("Maintenance Report", isPresented: $showReportAlert) {
|
||||
Button("OK", role: .cancel) { }
|
||||
|
||||
.alert(L10n.Residences.maintenanceReport, isPresented: $showReportAlert) {
|
||||
Button(L10n.Common.ok, role: .cancel) { }
|
||||
} message: {
|
||||
Text(viewModel.reportMessage ?? "")
|
||||
}
|
||||
@@ -189,7 +189,7 @@ private extension ResidenceDetailView {
|
||||
var loadingView: some View {
|
||||
VStack(spacing: 16) {
|
||||
ProgressView()
|
||||
Text("Loading residence...")
|
||||
Text(L10n.Residences.loadingResidence)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -226,9 +226,9 @@ private extension ResidenceDetailView {
|
||||
reloadTasks: { loadResidenceTasks() }
|
||||
)
|
||||
} else if isLoadingTasks {
|
||||
ProgressView("Loading tasks...")
|
||||
ProgressView(L10n.Residences.loadingTasks)
|
||||
} else if let tasksError = tasksError {
|
||||
Text("Error loading tasks: \(tasksError)")
|
||||
Text("\(L10n.Residences.errorLoadingTasks): \(tasksError)")
|
||||
.foregroundColor(Color.appError)
|
||||
.padding()
|
||||
}
|
||||
@@ -242,7 +242,7 @@ private extension ResidenceDetailView {
|
||||
Image(systemName: "person.2.fill")
|
||||
.font(.title2)
|
||||
.foregroundColor(Color.appPrimary)
|
||||
Text("Contractors")
|
||||
Text(L10n.Residences.contractors)
|
||||
.font(.title2.weight(.bold))
|
||||
.foregroundColor(Color.appPrimary)
|
||||
}
|
||||
@@ -256,7 +256,7 @@ private extension ResidenceDetailView {
|
||||
}
|
||||
.padding()
|
||||
} else if let error = contractorsError {
|
||||
Text("Error: \(error)")
|
||||
Text("\(L10n.Common.error): \(error)")
|
||||
.foregroundColor(Color.appError)
|
||||
.padding()
|
||||
} else if contractors.isEmpty {
|
||||
@@ -265,10 +265,10 @@ private extension ResidenceDetailView {
|
||||
Image(systemName: "person.crop.circle.badge.plus")
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(Color.appTextSecondary.opacity(0.6))
|
||||
Text("No contractors yet")
|
||||
Text(L10n.Residences.noContractors)
|
||||
.font(.headline)
|
||||
.foregroundColor(Color.appTextPrimary)
|
||||
Text("Add contractors from the Contractors tab")
|
||||
Text(L10n.Residences.addContractorsPrompt)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.appTextSecondary)
|
||||
}
|
||||
@@ -303,7 +303,7 @@ private extension ResidenceDetailView {
|
||||
var leadingToolbar: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
if viewModel.selectedResidence != nil {
|
||||
Button("Edit") {
|
||||
Button(L10n.Common.edit) {
|
||||
showEditResidence = true
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.editButton)
|
||||
|
||||
Reference in New Issue
Block a user