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:
@@ -48,7 +48,7 @@ struct ResidenceFormView: View {
|
||||
NavigationView {
|
||||
Form {
|
||||
Section {
|
||||
TextField("Property Name", text: $name)
|
||||
TextField(L10n.Residences.propertyName, text: $name)
|
||||
.focused($focusedField, equals: .name)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.nameField)
|
||||
|
||||
@@ -58,54 +58,54 @@ struct ResidenceFormView: View {
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
|
||||
Picker("Property Type", selection: $selectedPropertyType) {
|
||||
Text("Select Type").tag(nil as ResidenceType?)
|
||||
Picker(L10n.Residences.propertyType, selection: $selectedPropertyType) {
|
||||
Text(L10n.Residences.selectType).tag(nil as ResidenceType?)
|
||||
ForEach(residenceTypes, id: \.id) { type in
|
||||
Text(type.name).tag(type as ResidenceType?)
|
||||
}
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.propertyTypePicker)
|
||||
} header: {
|
||||
Text("Property Details")
|
||||
Text(L10n.Residences.propertyDetails)
|
||||
} footer: {
|
||||
Text("Required: Name")
|
||||
Text(L10n.Residences.requiredName)
|
||||
.font(.caption)
|
||||
.foregroundColor(Color.appError)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section {
|
||||
TextField("Street Address", text: $streetAddress)
|
||||
TextField(L10n.Residences.streetAddress, text: $streetAddress)
|
||||
.focused($focusedField, equals: .streetAddress)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.streetAddressField)
|
||||
|
||||
TextField("Apartment/Unit (optional)", text: $apartmentUnit)
|
||||
TextField(L10n.Residences.apartmentUnit, text: $apartmentUnit)
|
||||
.focused($focusedField, equals: .apartmentUnit)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.apartmentUnitField)
|
||||
|
||||
TextField("City", text: $city)
|
||||
TextField(L10n.Residences.city, text: $city)
|
||||
.focused($focusedField, equals: .city)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.cityField)
|
||||
|
||||
TextField("State/Province", text: $stateProvince)
|
||||
TextField(L10n.Residences.stateProvince, text: $stateProvince)
|
||||
.focused($focusedField, equals: .stateProvince)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.stateProvinceField)
|
||||
|
||||
TextField("Postal Code", text: $postalCode)
|
||||
TextField(L10n.Residences.postalCode, text: $postalCode)
|
||||
.focused($focusedField, equals: .postalCode)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.postalCodeField)
|
||||
|
||||
TextField("Country", text: $country)
|
||||
TextField(L10n.Residences.country, text: $country)
|
||||
.focused($focusedField, equals: .country)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.countryField)
|
||||
} header: {
|
||||
Text("Address")
|
||||
Text(L10n.Residences.address)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section(header: Text("Property Features")) {
|
||||
Section(header: Text(L10n.Residences.propertyFeatures)) {
|
||||
HStack {
|
||||
Text("Bedrooms")
|
||||
Text(L10n.Residences.bedrooms)
|
||||
Spacer()
|
||||
TextField("0", text: $bedrooms)
|
||||
.keyboardType(.numberPad)
|
||||
@@ -116,7 +116,7 @@ struct ResidenceFormView: View {
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Bathrooms")
|
||||
Text(L10n.Residences.bathrooms)
|
||||
Spacer()
|
||||
TextField("0.0", text: $bathrooms)
|
||||
.keyboardType(.decimalPad)
|
||||
@@ -126,29 +126,29 @@ struct ResidenceFormView: View {
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.bathroomsField)
|
||||
}
|
||||
|
||||
TextField("Square Footage", text: $squareFootage)
|
||||
TextField(L10n.Residences.squareFootage, text: $squareFootage)
|
||||
.keyboardType(.numberPad)
|
||||
.focused($focusedField, equals: .squareFootage)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.squareFootageField)
|
||||
|
||||
TextField("Lot Size (acres)", text: $lotSize)
|
||||
TextField(L10n.Residences.lotSize, text: $lotSize)
|
||||
.keyboardType(.decimalPad)
|
||||
.focused($focusedField, equals: .lotSize)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.lotSizeField)
|
||||
|
||||
TextField("Year Built", text: $yearBuilt)
|
||||
TextField(L10n.Residences.yearBuilt, text: $yearBuilt)
|
||||
.keyboardType(.numberPad)
|
||||
.focused($focusedField, equals: .yearBuilt)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.yearBuiltField)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
|
||||
Section(header: Text("Additional Details")) {
|
||||
TextField("Description (optional)", text: $description, axis: .vertical)
|
||||
Section(header: Text(L10n.Residences.additionalDetails)) {
|
||||
TextField(L10n.Residences.description, text: $description, axis: .vertical)
|
||||
.lineLimit(3...6)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.descriptionField)
|
||||
|
||||
Toggle("Primary Residence", isOn: $isPrimary)
|
||||
Toggle(L10n.Residences.primaryResidence, isOn: $isPrimary)
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.isPrimaryToggle)
|
||||
}
|
||||
.listRowBackground(Color.appBackgroundSecondary)
|
||||
@@ -165,18 +165,18 @@ struct ResidenceFormView: View {
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.appBackgroundPrimary)
|
||||
.navigationTitle(isEditMode ? "Edit Residence" : "Add Residence")
|
||||
.navigationTitle(isEditMode ? L10n.Residences.editTitle : L10n.Residences.addTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
Button(L10n.Common.cancel) {
|
||||
isPresented = false
|
||||
}
|
||||
.accessibilityIdentifier(AccessibilityIdentifiers.Residence.formCancelButton)
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Save") {
|
||||
Button(L10n.Common.save) {
|
||||
submitForm()
|
||||
}
|
||||
.disabled(!canSave || viewModel.isLoading)
|
||||
@@ -244,7 +244,7 @@ struct ResidenceFormView: View {
|
||||
var isValid = true
|
||||
|
||||
if name.isEmpty {
|
||||
nameError = "Name is required"
|
||||
nameError = L10n.Residences.nameRequired
|
||||
isValid = false
|
||||
} else {
|
||||
nameError = ""
|
||||
|
||||
Reference in New Issue
Block a user