From 03aa843f26134f99d5890e2d163230eb582df64e Mon Sep 17 00:00:00 2001 From: Stefan Lange-Hegermann Date: Fri, 3 Oct 2025 00:15:52 +0200 Subject: [PATCH] german and spanish translation --- Cable.xcodeproj/project.pbxproj | 2 + Cable/AppIcon.icon/Assets/box-2.png | Bin 0 -> 72264 bytes Cable/Base.lproj/Localizable.strings | 37 +++++++ Cable/Base.lproj/Localizable.stringsdict | 22 +++++ Cable/CableCalculator.swift | 2 +- Cable/CalculatorView.swift | 103 ++++++++++++++----- Cable/ComponentsOnboardingView.swift | 120 +++++++++++++++++++++++ Cable/ContentView.swift | 84 ++++++++++++---- Cable/LoadEditorView.swift | 6 +- Cable/OnboardingCarouselView.swift | 32 ++++++ Cable/SystemEditorView.swift | 12 ++- Cable/SystemsOnboardingView.swift | 4 +- Cable/UnitSystem.swift | 6 +- Cable/de.lproj/Localizable.strings | 104 ++++++++++++++++++++ Cable/de.lproj/Localizable.stringsdict | 22 +++++ Cable/es.lproj/Localizable.strings | 104 ++++++++++++++++++++ Cable/es.lproj/Localizable.stringsdict | 22 +++++ 17 files changed, 627 insertions(+), 55 deletions(-) create mode 100644 Cable/AppIcon.icon/Assets/box-2.png create mode 100644 Cable/Base.lproj/Localizable.strings create mode 100644 Cable/Base.lproj/Localizable.stringsdict create mode 100644 Cable/ComponentsOnboardingView.swift create mode 100644 Cable/OnboardingCarouselView.swift create mode 100644 Cable/de.lproj/Localizable.strings create mode 100644 Cable/de.lproj/Localizable.stringsdict create mode 100644 Cable/es.lproj/Localizable.strings create mode 100644 Cable/es.lproj/Localizable.stringsdict diff --git a/Cable.xcodeproj/project.pbxproj b/Cable.xcodeproj/project.pbxproj index 59e27dd..1576ad6 100644 --- a/Cable.xcodeproj/project.pbxproj +++ b/Cable.xcodeproj/project.pbxproj @@ -205,6 +205,8 @@ knownRegions = ( en, Base, + de, + es, ); mainGroup = 3E5C0BC32E72C0FD00247EC8; minimizedProjectReferenceProxies = 1; diff --git a/Cable/AppIcon.icon/Assets/box-2.png b/Cable/AppIcon.icon/Assets/box-2.png new file mode 100644 index 0000000000000000000000000000000000000000..ce339e719a4a027e0489e9431a18980724562794 GIT binary patch literal 72264 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

Il8NP-u#^1v!9_9kA2Pj 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 + + + + system.list.component.summary + + NSStringLocalizedFormatKey + %#@component_count@ • %@ + component_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d component + other + %d components + + + + diff --git a/Cable/CableCalculator.swift b/Cable/CableCalculator.swift index df99621..6c8e270 100644 --- a/Cable/CableCalculator.swift +++ b/Cable/CableCalculator.swift @@ -13,7 +13,7 @@ class CableCalculator: ObservableObject { @Published var current: Double = 5.0 @Published var power: Double = 60.0 @Published var length: Double = 10.0 - @Published var loadName: String = "My Load" + @Published var loadName: String = String(localized: "default.load.name", comment: "Default placeholder name for a load") var calculatedPower: Double { voltage * current diff --git a/Cable/CalculatorView.swift b/Cable/CalculatorView.swift index affc8ec..6e0e0fa 100644 --- a/Cable/CalculatorView.swift +++ b/Cable/CalculatorView.swift @@ -241,7 +241,7 @@ struct CalculatorView: View { let countryCode = rawCountryCode?.uppercased() let regionName = countryCode.flatMap { Locale.current.localizedString(forRegionCode: $0) ?? $0 } - let buttonTitle = "Review parts" + let buttonTitle = String(localized: "affiliate.button.review_parts", comment: "Button title to review a bill of materials before shopping") let identifier = "bom-\(savedLoad.name)-\(savedLoad.timestamp.timeIntervalSince1970)" return AffiliateLinkInfo( @@ -520,7 +520,14 @@ struct CalculatorView: View { } .buttonStyle(.plain) - Text(info.affiliateURL != nil ? "Tapping above shows a full bill of materials before opening the affiliate link. Purchases may support VoltPlan." : "Tapping above shows a full bill of materials with shopping searches to help you source parts.") + let descriptionKey = info.affiliateURL != nil + ? "affiliate.description.with_link" + : "affiliate.description.without_link" + let description = NSLocalizedString( + descriptionKey, + comment: "Explanation text beneath the affiliate button" + ) + Text(description) .font(.caption2) .foregroundColor(.secondary) } @@ -537,19 +544,36 @@ struct CalculatorView: View { let crossSectionValue = calculator.crossSection(for: unitSystem) let crossSectionLabel: String + let unknownSizeLabel = String(localized: "bom.size.unknown", comment: "Fallback label when the cable size is unknown") if unitSystem == .imperial { - crossSectionLabel = String(format: "AWG %.0f", crossSectionValue) + if crossSectionValue > 0 { + crossSectionLabel = String(format: "AWG %.0f", crossSectionValue) + } else { + crossSectionLabel = unknownSizeLabel + } } else { - crossSectionLabel = String(format: "%.1f mm²", crossSectionValue) + if crossSectionValue > 0 { + crossSectionLabel = String(format: "%.1f mm²", crossSectionValue) + } else { + crossSectionLabel = unknownSizeLabel + } } let cableDetail = "\(lengthLabel) • \(crossSectionLabel)" let powerDetail = String(format: "%.0f W @ %.1f V", calculator.calculatedPower, calculator.voltage) let fuseRating = calculator.recommendedFuse - let fuseDetail = "Inline holder and \(fuseRating)A fuse" + let fuseDetailFormat = NSLocalizedString( + "bom.fuse.detail", + comment: "Description for the fuse entry in the calculator BOM" + ) + let fuseDetail = String.localizedStringWithFormat(fuseDetailFormat, fuseRating) - let cableShoesDetail = "Ring or spade terminals sized for \(crossSectionLabel) wiring" + let cableShoesDetailFormat = NSLocalizedString( + "bom.terminals.detail", + comment: "Description for the cable terminals entry in the calculator BOM" + ) + let cableShoesDetail = String.localizedStringWithFormat(cableShoesDetailFormat, crossSectionLabel.lowercased()) let cableGaugeQuery: String if unitSystem == .imperial { @@ -568,10 +592,12 @@ struct CalculatorView: View { var items: [BOMItem] = [] + let fallbackComponentTitle = String(localized: "component.fallback.name", comment: "Fallback name for a component when no custom name is provided") + items.append( BOMItem( id: "component", - title: calculator.loadName.isEmpty ? "Component" : calculator.loadName, + title: calculator.loadName.isEmpty ? fallbackComponentTitle : calculator.loadName, detail: powerDetail, iconSystemName: "bolt.fill", destination: deviceLink.map { .affiliate($0) } ?? .amazonSearch(deviceQueryBase), @@ -582,7 +608,7 @@ struct CalculatorView: View { items.append( BOMItem( id: "cable-red", - title: "Power Cable (Red)", + title: String(localized: "bom.item.cable.red", comment: "Title for the red power cable item"), detail: cableDetail, iconSystemName: "bolt.horizontal.circle", destination: .amazonSearch(redCableQuery), @@ -593,7 +619,7 @@ struct CalculatorView: View { items.append( BOMItem( id: "cable-black", - title: "Power Cable (Black)", + title: String(localized: "bom.item.cable.black", comment: "Title for the black power cable item"), detail: cableDetail, iconSystemName: "bolt.horizontal.circle", destination: .amazonSearch(blackCableQuery), @@ -604,7 +630,7 @@ struct CalculatorView: View { items.append( BOMItem( id: "fuse", - title: "Fuse & Holder", + title: String(localized: "bom.item.fuse", comment: "Title for the fuse item"), detail: fuseDetail, iconSystemName: "bolt.shield", destination: .amazonSearch(fuseQuery), @@ -615,7 +641,7 @@ struct CalculatorView: View { items.append( BOMItem( id: "terminals", - title: "Cable Shoes / Terminals", + title: String(localized: "bom.item.terminals", comment: "Title for the terminals item"), detail: cableShoesDetail, iconSystemName: "wrench.and.screwdriver", destination: .amazonSearch(terminalQuery), @@ -636,7 +662,7 @@ struct CalculatorView: View { } private var voltageSlider: some View { - SliderSection(title: "Voltage", + SliderSection(title: String(localized: "slider.voltage.title", comment: "Title for the voltage slider"), value: $calculator.voltage, range: 3...48, unit: "V", @@ -656,11 +682,11 @@ struct CalculatorView: View { @ViewBuilder private var currentPowerSlider: some View { if isWattMode { - SliderSection(title: "Power", + SliderSection(title: String(localized: "slider.power.title", comment: "Title for the power slider"), value: $calculator.power, range: 0...2000, unit: "W", - buttonText: "Watt", + buttonText: String(localized: "slider.button.watt", comment: "Button label when showing power control"), buttonAction: { isWattMode = false calculator.updateFromPower() @@ -674,11 +700,11 @@ struct CalculatorView: View { autoUpdateSavedLoad() } } else { - SliderSection(title: "Current", + SliderSection(title: String(localized: "slider.current.title", comment: "Title for the current slider"), value: $calculator.current, range: 0...100, unit: "A", - buttonText: "Ampere", + buttonText: String(localized: "slider.button.ampere", comment: "Button label when showing current control"), buttonAction: { isWattMode = true calculator.updateFromCurrent() @@ -695,7 +721,11 @@ struct CalculatorView: View { } private var lengthSlider: some View { - SliderSection(title: "Cable Length (\(unitSettings.unitSystem.lengthUnit))", + let lengthTitleFormat = NSLocalizedString( + "slider.length.title", + comment: "Title format for the cable length slider" + ) + return SliderSection(title: String(format: lengthTitleFormat, locale: Locale.current, unitSettings.unitSystem.lengthUnit), value: $calculator.length, range: 0...20, unit: unitSettings.unitSystem.lengthUnit, @@ -711,8 +741,9 @@ struct CalculatorView: View { private func saveCurrentLoad() { + let fallbackName = String(localized: "default.load.unnamed", comment: "Fallback name for a load when no name is provided") let savedLoad = SavedLoad( - name: calculator.loadName.isEmpty ? "Unnamed Load" : calculator.loadName, + name: calculator.loadName.isEmpty ? fallbackName : calculator.loadName, voltage: calculator.voltage, current: calculator.current, power: calculator.power, @@ -740,7 +771,8 @@ struct CalculatorView: View { private func autoUpdateSavedLoad() { guard let savedLoad = savedLoad else { return } - savedLoad.name = calculator.loadName.isEmpty ? "Unnamed Load" : calculator.loadName + let fallbackName = String(localized: "default.load.unnamed", comment: "Fallback name for a load when no name is provided") + savedLoad.name = calculator.loadName.isEmpty ? fallbackName : calculator.loadName savedLoad.voltage = calculator.voltage savedLoad.current = calculator.current savedLoad.power = calculator.power @@ -776,6 +808,21 @@ private struct BillOfMaterialsView: View { ForEach(items) { item in let isCompleted = completedItemIDs.contains(item.id) let destinationURL = destinationURL(for: item) + let accessibilityLabel: String = { + if isCompleted { + let format = NSLocalizedString( + "bom.accessibility.mark.incomplete", + comment: "Accessibility label to mark a BOM item incomplete" + ) + return String.localizedStringWithFormat(format, item.title) + } else { + let format = NSLocalizedString( + "bom.accessibility.mark.complete", + comment: "Accessibility label to mark a BOM item complete" + ) + return String.localizedStringWithFormat(format, item.title) + } + }() HStack(spacing: 12) { Image(systemName: isCompleted ? "checkmark.circle.fill" : "circle") @@ -789,7 +836,7 @@ private struct BillOfMaterialsView: View { } suppressRowTapForID = item.id } - .accessibilityLabel(isCompleted ? "Mark \(item.title) incomplete" : "Mark \(item.title) complete") + .accessibilityLabel(accessibilityLabel) VStack(alignment: .leading, spacing: 4) { Text(item.title) @@ -798,7 +845,7 @@ private struct BillOfMaterialsView: View { .strikethrough(isCompleted, color: .accentColor.opacity(0.6)) if item.isPrimaryComponent { - Text("Component") + Text(String(localized: "component.fallback.name", comment: "Tag label marking a BOM entry as the main component")) .font(.caption2.weight(.medium)) .foregroundColor(.accentColor) .padding(.horizontal, 6) @@ -840,14 +887,24 @@ private struct BillOfMaterialsView: View { } Section { - Text("Purchases through affiliate links may support VoltPlan.") + Text( + String( + localized: "affiliate.disclaimer", + comment: "Footer note reminding users that affiliate purchases may support the app" + ) + ) .font(.footnote) .foregroundColor(.secondary) .padding(.vertical, 4) } } .listStyle(.insetGrouped) - .navigationTitle("Bill of Materials") + .navigationTitle( + String( + localized: "bom.navigation.title", + comment: "Navigation title for the bill of materials view" + ) + ) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { diff --git a/Cable/ComponentsOnboardingView.swift b/Cable/ComponentsOnboardingView.swift new file mode 100644 index 0000000..a0d9ce4 --- /dev/null +++ b/Cable/ComponentsOnboardingView.swift @@ -0,0 +1,120 @@ +import SwiftUI + +struct ComponentsOnboardingView: View { + @State private var carouselStep = 0 + let onCreate: () -> Void + let onBrowse: () -> Void + + private let imageNames = [ + "fridge-onboarding", + "coffee-onboarding", + "light-onboarding" + ] + + private let timer = Timer.publish(every: 8, on: .main, in: .common).autoconnect() + private let animationDuration = 0.8 + + private var loopingImages: [String] { + guard let first = imageNames.first else { return [] } + return imageNames + [first] + } + + var body: some View { + VStack { + Spacer(minLength: 32) + + OnboardingCarouselView(images: loopingImages, step: carouselStep) + .frame(minHeight: 80, maxHeight: 240) + .padding(.horizontal, 0) + + VStack(spacing: 12) { + Text("Add your first component") + .font(.title2.weight(.semibold)) + .multilineTextAlignment(.center) + + Text("Bring your system to life with components and let **Cable by VoltPlan** handle cable and fuse recommendations.") + .font(.body) + .foregroundStyle(Color.secondary) + .multilineTextAlignment(.center) + .frame(minHeight: 72) + .padding(.horizontal, 12) + } + .padding(.horizontal, 24) + + Spacer() + + VStack(spacing: 12) { + Button(action: createComponent) { + HStack(spacing: 8) { + Image(systemName: "plus.circle.fill") + .font(.system(size: 16)) + Text("Create Component") + .font(.headline.weight(.semibold)) + } + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background(Color.blue) + .cornerRadius(12) + } + .buttonStyle(.plain) + + Button(action: onBrowse) { + HStack(spacing: 8) { + Image(systemName: "books.vertical") + .font(.system(size: 16)) + Text("Browse Library") + .font(.headline.weight(.semibold)) + } + .foregroundColor(.blue) + .frame(maxWidth: .infinity) + .frame(height: 48) + .background( + RoundedRectangle(cornerRadius: 12, style: .continuous) + .fill(Color.blue.opacity(0.12)) + ) + .overlay( + RoundedRectangle(cornerRadius: 12, style: .continuous) + .stroke(Color.blue.opacity(0.24), lineWidth: 1) + ) + } + .buttonStyle(.plain) + } + .padding(.horizontal, 24) + } + .padding(.bottom, 32) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(.systemGroupedBackground)) + .onAppear(perform: resetState) + .onReceive(timer) { _ in advanceCarousel() } + } + + private func resetState() { + carouselStep = 0 + } + + private func createComponent() { + onCreate() + } + + private func advanceCarousel() { + guard imageNames.count > 1 else { return } + let next = carouselStep + 1 + + withAnimation(.easeInOut(duration: animationDuration)) { + carouselStep = next + } + + if next == imageNames.count { + DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) { + withAnimation(.none) { + carouselStep = 0 + } + } + } + } +} + +#Preview { + ComponentsOnboardingView(onCreate: {}, onBrowse: {}) +} diff --git a/Cable/ContentView.swift b/Cable/ContentView.swift index 5567cd6..3f9df20 100644 --- a/Cable/ContentView.swift +++ b/Cable/ContentView.swift @@ -192,7 +192,7 @@ struct SystemsView: View { private func makeSystem(preferredName: String? = nil, colorName: String? = nil, iconName: String? = nil) -> ElectricalSystem { let existingNames = Set(systems.map { $0.name }) let trimmedPreferred = preferredName?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" - let baseName = trimmedPreferred.isEmpty ? "New System" : trimmedPreferred + let baseName = trimmedPreferred.isEmpty ? String(localized: "default.system.new", comment: "Default name for a newly created system") : trimmedPreferred var systemName = baseName var counter = 2 @@ -221,7 +221,7 @@ struct SystemsView: View { } private func createLoad(from item: ComponentLibraryItem, in system: ElectricalSystem) -> SavedLoad { - let baseName = item.name.isEmpty ? "Library Load" : item.name + let baseName = item.name.isEmpty ? String(localized: "default.load.library", comment: "Default name when importing a library load") : item.name let loadName = uniqueLoadName(for: system, startingWith: baseName) let voltage = item.displayVoltage ?? 12.0 @@ -310,7 +310,9 @@ struct SystemsView: View { private func componentSummary(for system: ElectricalSystem) -> String { let systemLoads = loads(for: system) - guard !systemLoads.isEmpty else { return "No components yet" } + guard !systemLoads.isEmpty else { + return String(localized: "system.list.no.components", comment: "Message shown when a system has no components yet") + } let count = systemLoads.count let totalPower = systemLoads.reduce(0.0) { $0 + $1.power } @@ -322,7 +324,11 @@ struct SystemsView: View { formattedPower = String(format: "%.0fW", totalPower) } - return "\(count) component\(count == 1 ? "" : "s") • \(formattedPower) total" + let format = NSLocalizedString( + "system.list.component.summary", + comment: "Summary showing number of components and the total power" + ) + return String.localizedStringWithFormat(format, count, formattedPower) } private func randomSystemColorName() -> String { @@ -622,7 +628,8 @@ struct LoadsView: View { } private func createNewLoad() { - let loadName = uniqueLoadName(startingWith: "New Load") + let defaultName = String(localized: "default.load.new", comment: "Default name when creating a new load from system view") + let loadName = uniqueLoadName(startingWith: defaultName) let newLoad = SavedLoad( name: loadName, voltage: 12.0, @@ -903,6 +910,22 @@ private struct SystemBillOfMaterialsView: View { let destinationURL = destinationURL(for: item.destination, load: load) HStack(spacing: 12) { + let accessibilityLabel: String = { + if isCompleted { + let format = NSLocalizedString( + "bom.accessibility.mark.incomplete", + comment: "Accessibility label instructing VoiceOver to mark an item incomplete" + ) + return String.localizedStringWithFormat(format, item.title) + } else { + let format = NSLocalizedString( + "bom.accessibility.mark.complete", + comment: "Accessibility label instructing VoiceOver to mark an item complete" + ) + return String.localizedStringWithFormat(format, item.title) + } + }() + Image(systemName: isCompleted ? "checkmark.circle.fill" : "circle") .foregroundColor(isCompleted ? .accentColor : .secondary) .imageScale(.large) @@ -910,7 +933,7 @@ private struct SystemBillOfMaterialsView: View { setCompletion(!isCompleted, for: load, item: item) suppressRowTapForID = item.id } - .accessibilityLabel(isCompleted ? "Mark \(item.title) incomplete" : "Mark \(item.title) complete") + .accessibilityLabel(accessibilityLabel) VStack(alignment: .leading, spacing: 4) { Text(item.title) @@ -919,7 +942,7 @@ private struct SystemBillOfMaterialsView: View { .strikethrough(isCompleted, color: .accentColor.opacity(0.6)) if item.isPrimaryComponent { - Text("Component") + Text(String(localized: "component.fallback.name", comment: "Tag label marking an item as the component itself")) .font(.caption2.weight(.medium)) .foregroundColor(.accentColor) .padding(.horizontal, 6) @@ -971,7 +994,16 @@ private struct SystemBillOfMaterialsView: View { } } .listStyle(.insetGrouped) - .navigationTitle("BOM – \(systemName)") + .navigationTitle( + String( + format: NSLocalizedString( + "bom.navigation.title.system", + comment: "Navigation title for the bill of materials view" + ), + locale: Locale.current, + systemName + ) + ) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { @@ -995,7 +1027,8 @@ private struct SystemBillOfMaterialsView: View { private func sectionHeader(for load: SavedLoad) -> some View { VStack(alignment: .leading, spacing: 2) { - Text(load.name.isEmpty ? "Component" : load.name) + let fallbackTitle = String(localized: "component.fallback.name", comment: "Fallback title for a component that lacks a name") + Text(load.name.isEmpty ? fallbackTitle : load.name) .font(.headline) Text(dateFormatter.string(from: load.timestamp)) .font(.caption) @@ -1014,13 +1047,15 @@ private struct SystemBillOfMaterialsView: View { let crossSectionLabel: String let gaugeQuery: String + let unknownSizeLabel = String(localized: "bom.size.unknown", comment: "Fallback label when the cable size is not yet determined") + if unitSystem == .imperial { let awg = awgFromCrossSection(load.crossSection) if awg > 0 { crossSectionLabel = String(format: "AWG %.0f", awg) gaugeQuery = String(format: "AWG %.0f", awg) } else { - crossSectionLabel = "Size TBD" + crossSectionLabel = unknownSizeLabel gaugeQuery = "battery cable" } } else { @@ -1028,7 +1063,7 @@ private struct SystemBillOfMaterialsView: View { crossSectionLabel = String(format: "%.1f mm²", load.crossSection) gaugeQuery = String(format: "%.1f mm2", load.crossSection) } else { - crossSectionLabel = "Size TBD" + crossSectionLabel = unknownSizeLabel gaugeQuery = "battery cable" } } @@ -1039,9 +1074,17 @@ private struct SystemBillOfMaterialsView: View { let powerDetail = String(format: "%.0f W @ %.1f V", calculatedPower, load.voltage) let fuseRating = recommendedFuse(for: load) - let fuseDetail = "Inline holder and \(fuseRating)A fuse" + let fuseDetailFormat = NSLocalizedString( + "bom.fuse.detail", + comment: "Description for the fuse item in the BOM list" + ) + let fuseDetail = String.localizedStringWithFormat(fuseDetailFormat, fuseRating) - let cableShoesDetail = "Ring or spade terminals sized for \(crossSectionLabel.lowercased()) wiring" + let cableShoesDetailFormat = NSLocalizedString( + "bom.terminals.detail", + comment: "Description for the cable terminals item in the BOM list" + ) + let cableShoesDetail = String.localizedStringWithFormat(cableShoesDetailFormat, crossSectionLabel.lowercased()) let affiliateURL = load.affiliateURLString.flatMap { URL(string: $0) } let deviceQuery = load.name.isEmpty @@ -1057,7 +1100,7 @@ private struct SystemBillOfMaterialsView: View { Item( id: Self.storageKey(for: load, itemID: "component"), logicalID: "component", - title: load.name.isEmpty ? "Component" : load.name, + title: load.name.isEmpty ? String(localized: "component.fallback.name", comment: "Fallback name for a component when no name is provided") : load.name, detail: powerDetail, iconSystemName: "bolt.fill", destination: affiliateURL.map { .affiliate($0) } ?? .amazonSearch(deviceQuery), @@ -1066,7 +1109,7 @@ private struct SystemBillOfMaterialsView: View { Item( id: Self.storageKey(for: load, itemID: "cable-red"), logicalID: "cable-red", - title: "Power Cable (Red)", + title: String(localized: "bom.item.cable.red", comment: "Title for the red power cable item"), detail: cableDetail, iconSystemName: "bolt.horizontal.circle", destination: .amazonSearch(redCableQuery), @@ -1075,7 +1118,7 @@ private struct SystemBillOfMaterialsView: View { Item( id: Self.storageKey(for: load, itemID: "cable-black"), logicalID: "cable-black", - title: "Power Cable (Black)", + title: String(localized: "bom.item.cable.black", comment: "Title for the black power cable item"), detail: cableDetail, iconSystemName: "bolt.horizontal.circle", destination: .amazonSearch(blackCableQuery), @@ -1084,7 +1127,7 @@ private struct SystemBillOfMaterialsView: View { Item( id: Self.storageKey(for: load, itemID: "fuse"), logicalID: "fuse", - title: "Fuse & Holder", + title: String(localized: "bom.item.fuse", comment: "Title for the fuse and holder item"), detail: fuseDetail, iconSystemName: "bolt.shield", destination: .amazonSearch(fuseQuery), @@ -1093,7 +1136,7 @@ private struct SystemBillOfMaterialsView: View { Item( id: Self.storageKey(for: load, itemID: "terminals"), logicalID: "terminals", - title: "Cable Shoes / Terminals", + title: String(localized: "bom.item.terminals", comment: "Title for the cable terminals item"), detail: cableShoesDetail, iconSystemName: "wrench.and.screwdriver", destination: .amazonSearch(terminalQuery), @@ -1171,7 +1214,10 @@ private struct SystemBillOfMaterialsView: View { } private var footerMessage: String { - return "Purchases through affiliate links may support VoltPlan." + NSLocalizedString( + "affiliate.disclaimer", + comment: "Footer note reminding users that affiliate purchases may support the app" + ) } private var dateFormatter: DateFormatter { diff --git a/Cable/LoadEditorView.swift b/Cable/LoadEditorView.swift index c481498..7e2310c 100644 --- a/Cable/LoadEditorView.swift +++ b/Cable/LoadEditorView.swift @@ -23,9 +23,9 @@ struct LoadEditorView: View { var body: some View { ItemEditorView( - title: "Edit Load", - nameFieldLabel: "Load name", - previewSubtitle: "Preview", + title: String(localized: "editor.load.title", comment: "Title for the load editor"), + nameFieldLabel: String(localized: "editor.load.name_field", comment: "Label for the load name text field"), + previewSubtitle: String(localized: "editor.load.preview", comment: "Placeholder subtitle in the load editor preview"), icons: loadIcons, name: $loadName, iconName: $iconName, diff --git a/Cable/OnboardingCarouselView.swift b/Cable/OnboardingCarouselView.swift new file mode 100644 index 0000000..478ae40 --- /dev/null +++ b/Cable/OnboardingCarouselView.swift @@ -0,0 +1,32 @@ +import SwiftUI + +struct OnboardingCarouselView: View { + let images: [String] + let step: Int + + var body: some View { + GeometryReader { geometry in + let width = geometry.size.width + let height = geometry.size.height + + ZStack { + if images.isEmpty { + Image(systemName: "photo") + .font(.largeTitle) + .foregroundStyle(Color.secondary) + } else { + HStack(spacing: 0) { + ForEach(Array(images.enumerated()), id: \.offset) { _, name in + Image(name) + .resizable() + .scaledToFit() + .frame(width: width, height: height) + } + } + .offset(x: -CGFloat(step) * width) + } + } + .clipped() + } + } +} diff --git a/Cable/SystemEditorView.swift b/Cable/SystemEditorView.swift index 9de0bb5..98f7924 100644 --- a/Cable/SystemEditorView.swift +++ b/Cable/SystemEditorView.swift @@ -32,17 +32,21 @@ struct SystemEditorView: View { } var body: some View { + let editorTitle = String(localized: "editor.system.title", comment: "Title for the system editor") + let namePlaceholder = String(localized: "editor.system.name_field", comment: "Label for the system name text field") + let locationPlaceholder = String(localized: "editor.system.location.optional", comment: "Placeholder text shown when no location is specified") + ItemEditorView( - title: "Edit System", - nameFieldLabel: "System name", - previewSubtitle: tempLocation.isEmpty ? "Location (optional)" : tempLocation, + title: editorTitle, + nameFieldLabel: namePlaceholder, + previewSubtitle: tempLocation.isEmpty ? locationPlaceholder : tempLocation, icons: systemIcons, name: $systemName, iconName: $iconName, colorName: $colorName, additionalFields: { AnyView( - TextField("Location (optional)", text: $tempLocation) + TextField(locationPlaceholder, text: $tempLocation) .autocapitalization(.words) .onChange(of: tempLocation) { _, newValue in location = newValue diff --git a/Cable/SystemsOnboardingView.swift b/Cable/SystemsOnboardingView.swift index 904bcbc..7146fe8 100644 --- a/Cable/SystemsOnboardingView.swift +++ b/Cable/SystemsOnboardingView.swift @@ -1,7 +1,7 @@ import SwiftUI struct SystemsOnboardingView: View { - @State private var systemName: String = "My System" + @State private var systemName: String = String(localized: "default.system.name", comment: "Default placeholder name for a system") @State private var carouselStep = 0 @FocusState private var isFieldFocused: Bool let onCreate: (String) -> Void @@ -94,7 +94,7 @@ struct SystemsOnboardingView: View { } private func resetState() { - systemName = "My System" + systemName = String(localized: "default.system.name", comment: "Default placeholder name for a system") carouselStep = 0 } diff --git a/Cable/UnitSystem.swift b/Cable/UnitSystem.swift index 1336221..beec220 100644 --- a/Cable/UnitSystem.swift +++ b/Cable/UnitSystem.swift @@ -14,9 +14,9 @@ enum UnitSystem: String, CaseIterable { var displayName: String { switch self { case .metric: - return "Metric (mm², m)" + return String(localized: "units.metric.display", comment: "Display name for the metric unit system") case .imperial: - return "Imperial (AWG, ft)" + return String(localized: "units.imperial.display", comment: "Display name for the imperial unit system") } } @@ -51,4 +51,4 @@ class UnitSystemSettings: ObservableObject { let savedSystem = UserDefaults.standard.string(forKey: "unitSystem") ?? UnitSystem.metric.rawValue self.unitSystem = UnitSystem(rawValue: savedSystem) ?? .metric } -} \ No newline at end of file +} diff --git a/Cable/de.lproj/Localizable.strings b/Cable/de.lproj/Localizable.strings new file mode 100644 index 0000000..28ff559 --- /dev/null +++ b/Cable/de.lproj/Localizable.strings @@ -0,0 +1,104 @@ +// Keys +"affiliate.button.review_parts" = "Teile prüfen"; +"affiliate.description.with_link" = "Tippen oben zeigt eine vollständige Stückliste, bevor der Affiliate-Link geöffnet wird. Käufe können VoltPlan unterstützen."; +"affiliate.description.without_link" = "Tippen oben zeigt eine vollständige Stückliste mit Einkaufssuchen, die dir bei der Beschaffung helfen."; +"affiliate.disclaimer" = "Käufe über Affiliate-Links können VoltPlan unterstützen."; +"bom.accessibility.mark.complete" = "Markiere %@ als erledigt"; +"bom.accessibility.mark.incomplete" = "Markiere %@ als unerledigt"; +"bom.fuse.detail" = "Inline-Halter und %dA-Sicherung"; +"bom.item.cable.black" = "Stromkabel (schwarz)"; +"bom.item.cable.red" = "Stromkabel (rot)"; +"bom.item.fuse" = "Sicherung & Halter"; +"bom.item.terminals" = "Kabelschuhe / Klemmen"; +"bom.navigation.title" = "Stückliste"; +"bom.navigation.title.system" = "Stückliste – %@"; +"bom.size.unknown" = "Größe offen"; +"bom.terminals.detail" = "Ring- oder Gabelkabelschuhe für %@-Leitungen"; +"component.fallback.name" = "Komponente"; +"default.load.library" = "Bibliothekslast"; +"default.load.name" = "Mein Verbraucher"; +"default.load.unnamed" = "Unbenannter Verbraucher"; +"default.load.new" = "Neuer Verbraucher"; +"default.system.name" = "Mein System"; +"default.system.new" = "Neues System"; +"editor.load.name_field" = "Name des Verbrauchers"; +"editor.load.preview" = "Vorschau"; +"editor.load.title" = "Verbraucher bearbeiten"; +"editor.system.location.optional" = "Standort (optional)"; +"editor.system.name_field" = "Name des Systems"; +"editor.system.title" = "System bearbeiten"; +"slider.button.ampere" = "Ampere"; +"slider.button.watt" = "Watt"; +"slider.current.title" = "Strom"; +"slider.length.title" = "Kabellänge (%@)"; +"slider.power.title" = "Leistung"; +"slider.voltage.title" = "Spannung"; +"system.list.no.components" = "Noch keine Komponenten"; +"units.imperial.display" = "Imperial (AWG, ft)"; +"units.metric.display" = "Metrisch (mm², m)"; + +// Direct strings +"Systems" = "Systeme"; +"System" = "System"; +"System View" = "Systemansicht"; +"System Name" = "Systemname"; +"Create System" = "System erstellen"; +"Create your first system" = "Erstelle dein erstes System"; +"Give your setup a name so **Cable by VoltPlan** can organize loads, wiring, and recommendations in one place." = "Gib deinem Aufbau einen Namen, damit **Cable by VoltPlan** Verbraucher, Leitungen und Empfehlungen an einem Ort organisiert."; +"Add your first component" = "Füge deine erste Komponente hinzu"; +"Bring your system to life with components and let **Cable by VoltPlan** handle cable and fuse recommendations." = "Erwecke dein System mit Komponenten zum Leben und überlasse **Cable by VoltPlan** die Kabel- und Sicherungsempfehlungen."; +"Create Component" = "Komponente erstellen"; +"Browse Library" = "Bibliothek durchsuchen"; +"Browse" = "Durchsuchen"; +"Browse electrical components from VoltPlan" = "Elektrische Komponenten von VoltPlan durchstöbern"; +"Component Library" = "Komponentenbibliothek"; +"Details coming soon" = "Details folgen in Kürze"; +"Components" = "Komponenten"; +"FUSE" = "SICHERUNG"; +"WIRE" = "KABEL"; +"Current" = "Strom"; +"Power" = "Leistung"; +"Voltage" = "Spannung"; +"Length" = "Länge"; +"Length:" = "Länge:"; +"Wire Cross-Section:" = "Kabelquerschnitt:"; +"Current Units" = "Aktuelle Einheiten"; +"Unit System" = "Einheitensystem"; +"Units" = "Einheiten"; +"Settings" = "Einstellungen"; +"Close" = "Schließen"; +"Cancel" = "Abbrechen"; +"Save" = "Speichern"; +"Retry" = "Erneut versuchen"; +"Loading components" = "Komponenten werden geladen"; +"Unable to load components" = "Komponenten konnten nicht geladen werden"; +"No components available" = "Keine Komponenten verfügbar"; +"No matches" = "Keine Treffer"; +"Check back soon for new loads from VoltPlan." = "Schau bald wieder vorbei, um neue Verbraucher von VoltPlan zu finden."; +"Try searching for a different name." = "Versuche, nach einem anderen Namen zu suchen."; +"Search components" = "Komponenten suchen"; +"No loads saved in this system yet." = "In diesem System sind noch keine Verbraucher gespeichert."; +"Coming soon - manage your electrical systems and panels here." = "Demnächst verfügbar – verwalte hier deine elektrischen Systeme und Verteilungen."; +"Load Library" = "Verbraucher-bibliothek"; +"Safety Disclaimer" = "Sicherheitshinweis"; +"This application provides electrical calculations for educational and estimation purposes only." = "Diese Anwendung stellt elektrische Berechnungen nur zu Schulungs- und Schätzungszwecken bereit."; +"Important:" = "Wichtig:"; +"• Always consult qualified electricians for actual installations" = "• Ziehe für tatsächliche Installationen stets qualifizierte Elektriker hinzu"; +"• Follow all local electrical codes and regulations" = "• Beachte alle örtlichen Vorschriften und Normen"; +"• Electrical work should only be performed by licensed professionals" = "• Elektroarbeiten sollten nur von zugelassenen Fachkräften ausgeführt werden"; +"• These calculations may not account for all environmental factors" = "• Diese Berechnungen berücksichtigen möglicherweise nicht alle Umgebungsfaktoren"; +"• The app developers assume no liability for electrical installations" = "• Die App-Entwickler übernehmen keine Haftung für elektrische Installationen"; +"Enter length in %@" = "Gib die Länge in %@ ein"; +"Enter voltage in volts (V)" = "Gib die Spannung in Volt (V) ein"; +"Enter current in amperes (A)" = "Gib den Strom in Ampere (A) ein"; +"Enter power in watts (W)" = "Gib die Leistung in Watt (W) ein"; +"Edit Length" = "Länge bearbeiten"; +"Edit Voltage" = "Spannung bearbeiten"; +"Edit Current" = "Strom bearbeiten"; +"Edit Power" = "Leistung bearbeiten"; +"Preview" = "Vorschau"; +"Details" = "Details"; +"Icon" = "Symbol"; +"Color" = "Farbe"; +"VoltPlan Library" = "VoltPlan-Bibliothek"; +"New Load" = "Neuer Verbraucher"; diff --git a/Cable/de.lproj/Localizable.stringsdict b/Cable/de.lproj/Localizable.stringsdict new file mode 100644 index 0000000..eea5ef1 --- /dev/null +++ b/Cable/de.lproj/Localizable.stringsdict @@ -0,0 +1,22 @@ + + + + + system.list.component.summary + + NSStringLocalizedFormatKey + %#@component_count@ • %@ + component_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d Komponente + other + %d Komponenten + + + + diff --git a/Cable/es.lproj/Localizable.strings b/Cable/es.lproj/Localizable.strings new file mode 100644 index 0000000..a2e18e4 --- /dev/null +++ b/Cable/es.lproj/Localizable.strings @@ -0,0 +1,104 @@ +// Keys +"affiliate.button.review_parts" = "Revisar componentes"; +"affiliate.description.with_link" = "Al tocar arriba se muestra una lista completa de materiales antes de abrir el enlace de afiliado. Las compras pueden ayudar a VoltPlan."; +"affiliate.description.without_link" = "Al tocar arriba se muestra una lista completa de materiales con búsquedas de compra para ayudarte a conseguir piezas."; +"affiliate.disclaimer" = "Las compras a través de enlaces de afiliados pueden ayudar a VoltPlan."; +"bom.accessibility.mark.complete" = "Marcar %@ como completado"; +"bom.accessibility.mark.incomplete" = "Marcar %@ como pendiente"; +"bom.fuse.detail" = "Portafusibles en línea y fusible de %d A"; +"bom.item.cable.black" = "Cable de alimentación (negro)"; +"bom.item.cable.red" = "Cable de alimentación (rojo)"; +"bom.item.fuse" = "Fusible y portafusibles"; +"bom.item.terminals" = "Terminales / Zapatas"; +"bom.navigation.title" = "Lista de materiales"; +"bom.navigation.title.system" = "Lista de materiales – %@"; +"bom.size.unknown" = "Tamaño por definir"; +"bom.terminals.detail" = "Terminales de anillo o horquilla para cables de %@"; +"component.fallback.name" = "Componente"; +"default.load.library" = "Carga de biblioteca"; +"default.load.name" = "Mi carga"; +"default.load.unnamed" = "Carga sin nombre"; +"default.load.new" = "Carga nueva"; +"default.system.name" = "Mi sistema"; +"default.system.new" = "Sistema nuevo"; +"editor.load.name_field" = "Nombre de la carga"; +"editor.load.preview" = "Vista previa"; +"editor.load.title" = "Editar carga"; +"editor.system.location.optional" = "Ubicación (opcional)"; +"editor.system.name_field" = "Nombre del sistema"; +"editor.system.title" = "Editar sistema"; +"slider.button.ampere" = "Amperios"; +"slider.button.watt" = "Vatios"; +"slider.current.title" = "Corriente"; +"slider.length.title" = "Longitud del cable (%@)"; +"slider.power.title" = "Potencia"; +"slider.voltage.title" = "Voltaje"; +"system.list.no.components" = "Aún no hay componentes"; +"units.imperial.display" = "Imperial (AWG, ft)"; +"units.metric.display" = "Métrico (mm², m)"; + +// Direct strings +"Systems" = "Sistemas"; +"System" = "Sistema"; +"System View" = "Vista del sistema"; +"System Name" = "Nombre del sistema"; +"Create System" = "Crear sistema"; +"Create your first system" = "Crea tu primer sistema"; +"Give your setup a name so **Cable by VoltPlan** can organize loads, wiring, and recommendations in one place." = "Ponle un nombre a tu instalación para que **Cable by VoltPlan** organice cargas, cableado y recomendaciones en un solo lugar."; +"Add your first component" = "Añade tu primer componente"; +"Bring your system to life with components and let **Cable by VoltPlan** handle cable and fuse recommendations." = "Da vida a tu sistema con componentes y deja que **Cable by VoltPlan** se encargue de recomendar cables y fusibles."; +"Create Component" = "Crear componente"; +"Browse Library" = "Explorar biblioteca"; +"Browse" = "Explorar"; +"Browse electrical components from VoltPlan" = "Explora los componentes eléctricos de VoltPlan"; +"Component Library" = "Biblioteca de componentes"; +"Details coming soon" = "Detalles próximamente"; +"Components" = "Componentes"; +"FUSE" = "FUSIBLE"; +"WIRE" = "CABLE"; +"Current" = "Corriente"; +"Power" = "Potencia"; +"Voltage" = "Voltaje"; +"Length" = "Longitud"; +"Length:" = "Longitud:"; +"Wire Cross-Section:" = "Sección del cable:"; +"Current Units" = "Unidades actuales"; +"Unit System" = "Sistema de unidades"; +"Units" = "Unidades"; +"Settings" = "Ajustes"; +"Close" = "Cerrar"; +"Cancel" = "Cancelar"; +"Save" = "Guardar"; +"Retry" = "Reintentar"; +"Loading components" = "Cargando componentes"; +"Unable to load components" = "No se pudieron cargar los componentes"; +"No components available" = "No hay componentes disponibles"; +"No matches" = "Sin coincidencias"; +"Check back soon for new loads from VoltPlan." = "Vuelve pronto para encontrar nuevas cargas de VoltPlan."; +"Try searching for a different name." = "Prueba a buscar otro nombre."; +"Search components" = "Buscar componentes"; +"No loads saved in this system yet." = "Todavía no hay cargas guardadas en este sistema."; +"Coming soon - manage your electrical systems and panels here." = "Próximamente: gestiona aquí tus sistemas y paneles eléctricos."; +"Load Library" = "Biblioteca de cargas"; +"Safety Disclaimer" = "Aviso de seguridad"; +"This application provides electrical calculations for educational and estimation purposes only." = "Esta aplicación proporciona cálculos eléctricos únicamente con fines educativos y de estimación."; +"Important:" = "Importante:"; +"• Always consult qualified electricians for actual installations" = "• Consulta siempre a electricistas cualificados para las instalaciones reales"; +"• Follow all local electrical codes and regulations" = "• Cumple todas las normativas y códigos eléctricos locales"; +"• Electrical work should only be performed by licensed professionals" = "• Los trabajos eléctricos solo deben realizarlos profesionales autorizados"; +"• These calculations may not account for all environmental factors" = "• Estos cálculos pueden no tener en cuenta todos los factores ambientales"; +"• The app developers assume no liability for electrical installations" = "• Los desarrolladores de la app no asumen responsabilidad por las instalaciones eléctricas"; +"Enter length in %@" = "Introduce la longitud en %@"; +"Enter voltage in volts (V)" = "Introduce el voltaje en voltios (V)"; +"Enter current in amperes (A)" = "Introduce la corriente en amperios (A)"; +"Enter power in watts (W)" = "Introduce la potencia en vatios (W)"; +"Edit Length" = "Editar longitud"; +"Edit Voltage" = "Editar voltaje"; +"Edit Current" = "Editar corriente"; +"Edit Power" = "Editar potencia"; +"Preview" = "Vista previa"; +"Details" = "Detalles"; +"Icon" = "Icono"; +"Color" = "Color"; +"VoltPlan Library" = "Biblioteca de VoltPlan"; +"New Load" = "Carga nueva"; diff --git a/Cable/es.lproj/Localizable.stringsdict b/Cable/es.lproj/Localizable.stringsdict new file mode 100644 index 0000000..0ec9174 --- /dev/null +++ b/Cable/es.lproj/Localizable.stringsdict @@ -0,0 +1,22 @@ + + + + + system.list.component.summary + + NSStringLocalizedFormatKey + %#@component_count@ • %@ + component_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d componente + other + %d componentes + + + +