Fix AWG notation, add alternator type, migrate to String(localized:)

- Fix AWG 0/00/000/0000 bug (all resolved to 0 in Swift) using negative
  int convention (-1 through -4) with formatAWG() for 1/0–4/0 display
- Add 7.5A fuse size and change fuse type from Int to Double
- Add alternator power source type with distinct bolt.car.fill icon
- Migrate all NSLocalizedString calls to String(localized:defaultValue:)
- Update translations for runtime subtitle (ES/FR/NL: current→maximum),
  usable capacity footer text, and NL override wording
- Store length always in meters, convert at display time in CalculatorView
- Add preview-friendly inits for ComponentLibraryView and LoadsView
- Expand test coverage for calculations, fuses, AWG, and edge cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-27 10:37:53 +01:00
parent 03878b9507
commit ea3b60d75c
24 changed files with 899 additions and 928 deletions

View File

@@ -398,11 +398,9 @@ struct SystemOverviewView: View {
message: Text(detail.message),
dismissButton: .default(
Text(
NSLocalizedString(
"battery.bank.status.dismiss",
bundle: .main,
value: "Got it",
comment: "Dismiss button title for load status alert"
String(
localized: "battery.bank.status.dismiss",
defaultValue: "Got it"
)
)
)
@@ -962,266 +960,123 @@ struct SystemOverviewView: View {
}
private var loadsSummaryTitle: String {
NSLocalizedString(
"loads.overview.header.title",
bundle: .main,
value: "Load Overview",
comment: "Title for the loads overview summary section"
)
String(localized: "loads.overview.header.title", defaultValue: "Load Overview")
}
private var loadsCountLabel: String {
NSLocalizedString(
"loads.overview.metric.count",
bundle: .main,
value: "Loads",
comment: "Label for number of loads metric"
)
String(localized: "loads.overview.metric.count", defaultValue: "Loads")
}
private var loadsCurrentLabel: String {
NSLocalizedString(
"loads.overview.metric.current",
bundle: .main,
value: "Total Current",
comment: "Label for total load current metric"
)
String(localized: "loads.overview.metric.current", defaultValue: "Total Current")
}
private var loadsPowerLabel: String {
NSLocalizedString(
"loads.overview.metric.power",
bundle: .main,
value: "Total Power",
comment: "Label for total load power metric"
)
String(localized: "loads.overview.metric.power", defaultValue: "Total Power")
}
private var loadsEmptyTitle: String {
NSLocalizedString(
"overview.loads.empty.title",
bundle: .main,
value: "No loads configured yet",
comment: "Title shown in overview when no loads exist"
)
String(localized: "overview.loads.empty.title", defaultValue: "No loads configured yet")
}
private var loadsEmptySubtitle: String {
NSLocalizedString(
"overview.loads.empty.subtitle",
bundle: .main,
value: "Add components to get cable sizing and fuse recommendations tailored to this system.",
comment: "Subtitle shown in overview when no loads exist"
)
String(localized: "overview.loads.empty.subtitle", defaultValue: "Add components to get cable sizing and fuse recommendations tailored to this system.")
}
private var loadsEmptyMessage: String {
NSLocalizedString(
"loads.overview.empty.message",
bundle: .main,
value: "Start by adding a load to see system insights.",
comment: "Message shown when no loads exist"
)
String(localized: "loads.overview.empty.message", defaultValue: "Start by adding a load to see system insights.")
}
private var loadsEmptyCreateAction: String {
NSLocalizedString(
"loads.overview.empty.create",
bundle: .main,
value: "Create Load",
comment: "Button title to create a new load"
)
String(localized: "loads.overview.empty.create", defaultValue: "Add Load")
}
private var loadsEmptyBrowseAction: String {
NSLocalizedString(
"loads.overview.empty.library",
bundle: .main,
value: "Browse Library",
comment: "Button title to open load library"
)
String(localized: "loads.overview.empty.library", defaultValue: "Browse Library")
}
private var batterySummaryTitle: String {
NSLocalizedString(
"battery.bank.header.title",
bundle: .main,
value: "Battery Bank",
comment: "Title for the battery bank summary section"
)
String(localized: "battery.bank.header.title", defaultValue: "Battery Bank")
}
private var batteryCountLabel: String {
NSLocalizedString(
"battery.bank.metric.count",
bundle: .main,
value: "Batteries",
comment: "Label for number of batteries metric"
)
String(localized: "battery.bank.metric.count", defaultValue: "Batteries")
}
private var batteryCapacityLabel: String {
NSLocalizedString(
"battery.bank.metric.capacity",
bundle: .main,
value: "Capacity",
comment: "Label for total capacity metric"
)
String(localized: "battery.bank.metric.capacity", defaultValue: "Capacity")
}
private var batteryUsableCapacityLabel: String {
NSLocalizedString(
"battery.bank.metric.usable_capacity",
bundle: .main,
value: "Usable Capacity",
comment: "Label for usable capacity metric"
)
String(localized: "battery.bank.metric.usable_capacity", defaultValue: "Usable Capacity")
}
private var batteryUsableEnergyLabel: String {
NSLocalizedString(
"battery.bank.metric.usable_energy",
bundle: .main,
value: "Usable Energy",
comment: "Label for usable energy metric"
)
String(localized: "battery.bank.metric.usable_energy", defaultValue: "Usable Energy")
}
private var batteryEmptyTitle: String {
NSLocalizedString(
"battery.bank.empty.title",
bundle: .main,
value: "No Batteries Yet",
comment: "Title shown when no batteries are configured"
)
String(localized: "battery.bank.empty.title", defaultValue: "No Batteries Yet")
}
private var batteryEmptySubtitle: String {
let format = NSLocalizedString(
"battery.bank.empty.subtitle",
tableName: nil,
bundle: .main,
value: "Tap the plus button to configure a battery for %@.",
comment: "Subtitle shown when no batteries are configured"
let format = String(
localized: "battery.bank.empty.subtitle",
defaultValue: "Tap the plus button to configure a battery for %@."
)
return String(format: format, system.name)
}
private var batteryEmptyCreateAction: String {
NSLocalizedString(
"battery.overview.empty.create",
bundle: .main,
value: "Create Battery",
comment: "Button title to create a new battery"
)
String(localized: "battery.overview.empty.create", defaultValue: "Add Battery")
}
private var chargerSummaryTitle: String {
NSLocalizedString(
"overview.chargers.header.title",
bundle: .main,
value: "Charger Overview",
comment: "Title for the chargers summary section"
)
String(localized: "overview.chargers.header.title", defaultValue: "Charger Overview")
}
private var chargerCountLabel: String {
NSLocalizedString(
"chargers.summary.metric.count",
bundle: .main,
value: "Chargers",
comment: "Label for number of chargers metric"
)
String(localized: "chargers.summary.metric.count", defaultValue: "Chargers")
}
private var chargerOutputLabel: String {
NSLocalizedString(
"chargers.summary.metric.output",
bundle: .main,
value: "Output Voltage",
comment: "Label for representative output voltage metric"
)
String(localized: "chargers.summary.metric.output", defaultValue: "Output Voltage")
}
private var chargerCurrentLabel: String {
NSLocalizedString(
"chargers.summary.metric.current",
bundle: .main,
value: "Charge Rate",
comment: "Label for total charger current metric"
)
String(localized: "chargers.summary.metric.current", defaultValue: "Charge Rate")
}
private var chargerPowerLabel: String {
NSLocalizedString(
"chargers.summary.metric.power",
bundle: .main,
value: "Charge Power",
comment: "Label for total charger power metric"
)
String(localized: "chargers.summary.metric.power", defaultValue: "Charge Power")
}
private var chargerEmptyTitle: String {
NSLocalizedString(
"overview.chargers.empty.title",
bundle: .main,
value: "No chargers configured yet",
comment: "Title shown when no chargers are configured"
)
String(localized: "overview.chargers.empty.title", defaultValue: "No chargers configured yet")
}
private var chargerEmptySubtitle: String {
NSLocalizedString(
"overview.chargers.empty.subtitle",
bundle: .main,
value: "Add shore power, DC-DC, or solar chargers to understand your charging capacity.",
comment: "Subtitle shown when no chargers are configured"
)
String(localized: "overview.chargers.empty.subtitle", defaultValue: "Add shore power, DC-DC, or solar chargers to understand your charging capacity.")
}
private var chargerEmptyCreateAction: String {
NSLocalizedString(
"overview.chargers.empty.create",
bundle: .main,
value: "Add Charger",
comment: "Button title to create a charger from the overview"
)
String(localized: "overview.chargers.empty.create", defaultValue: "Add Charger")
}
private var systemOverviewTitle: String {
NSLocalizedString(
"overview.system.header.title",
bundle: .main,
value: "System Overview",
comment: "Title for system overview card"
)
String(localized: "overview.system.header.title", defaultValue: "System Overview")
}
private var bomTitle: String {
NSLocalizedString(
"overview.bom.title",
bundle: .main,
value: "Bill of Materials",
comment: "Title for BOM metric card in the system overview"
)
String(localized: "overview.bom.title", defaultValue: "Bill of Materials")
}
private var bomSubtitle: String {
NSLocalizedString(
"overview.bom.subtitle",
bundle: .main,
value: "Tap to review components",
comment: "Subtitle describing the BOM metric card interaction"
)
String(localized: "overview.bom.subtitle", defaultValue: "Tap to review components")
}
private var bomPlaceholderSummary: String {
NSLocalizedString(
"overview.bom.placeholder.short",
bundle: .main,
value: "Add loads",
comment: "Short placeholder shown when no BOM data is available"
)
String(localized: "overview.bom.placeholder.short", defaultValue: "Add loads")
}
private var formattedBOMCompletedCount: String? {
@@ -1235,30 +1090,15 @@ struct SystemOverviewView: View {
}
private var chargeTimeTitle: String {
NSLocalizedString(
"overview.chargetime.title",
bundle: .main,
value: "Estimated charge time",
comment: "Title for the charge time metric card"
)
String(localized: "overview.chargetime.title", defaultValue: "Estimated charge time")
}
private var chargeTimeSubtitle: String {
NSLocalizedString(
"overview.chargetime.subtitle",
bundle: .main,
value: "At combined charge rate",
comment: "Subtitle describing charge time assumptions"
)
String(localized: "overview.chargetime.subtitle", defaultValue: "At combined charge rate")
}
private var chargeTimePlaceholderSummary: String {
NSLocalizedString(
"overview.chargetime.placeholder.short",
bundle: .main,
value: "Add chargers",
comment: "Short placeholder shown when charge time cannot be calculated"
)
String(localized: "overview.chargetime.placeholder.short", defaultValue: "Add chargers")
}
private var chargeGoalValueText: String? {
@@ -1266,39 +1106,19 @@ struct SystemOverviewView: View {
}
private var goalPrefix: String {
NSLocalizedString(
"overview.goal.prefix",
bundle: .main,
value: "Goal",
comment: "Prefix displayed before goal value"
)
String(localized: "overview.goal.prefix", defaultValue: "Goal")
}
private var runtimeTitle: String {
NSLocalizedString(
"overview.runtime.title",
bundle: .main,
value: "Estimated runtime",
comment: "Title for estimated runtime section"
)
String(localized: "overview.runtime.title", defaultValue: "Estimated runtime")
}
private var runtimeSubtitle: String {
NSLocalizedString(
"overview.runtime.subtitle",
bundle: .main,
value: "At current load draw",
comment: "Subtitle describing runtime assumption"
)
String(localized: "overview.runtime.subtitle", defaultValue: "At maximum load draw")
}
private var runtimePlaceholderSummary: String {
NSLocalizedString(
"overview.runtime.placeholder.short",
bundle: .main,
value: "Add capacity",
comment: "Short placeholder shown when runtime cannot be calculated"
)
String(localized: "overview.runtime.placeholder.short", defaultValue: "Add capacity")
}
private var runtimeGoalValueText: String? {
@@ -1311,48 +1131,23 @@ struct SystemOverviewView: View {
}
private var runtimeGoalSheetTitle: String {
NSLocalizedString(
"overview.runtime.goal.title",
bundle: .main,
value: "Runtime Goal",
comment: "Navigation title for editing the runtime goal"
)
String(localized: "overview.runtime.goal.title", defaultValue: "Runtime Goal")
}
private var chargeGoalSheetTitle: String {
NSLocalizedString(
"overview.chargetime.goal.title",
bundle: .main,
value: "Charge Goal",
comment: "Navigation title for editing the charge time goal"
)
String(localized: "overview.chargetime.goal.title", defaultValue: "Charge Goal")
}
private var goalClearTitle: String {
NSLocalizedString(
"overview.goal.clear",
bundle: .main,
value: "Remove Goal",
comment: "Button title to clear an active goal"
)
String(localized: "overview.goal.clear", defaultValue: "Remove Goal")
}
private var goalCancelTitle: String {
NSLocalizedString(
"overview.goal.cancel",
bundle: .main,
value: "Cancel",
comment: "Button title to cancel goal editing"
)
String(localized: "overview.goal.cancel", defaultValue: "Cancel")
}
private var goalSaveTitle: String {
NSLocalizedString(
"overview.goal.save",
bundle: .main,
value: "Save",
comment: "Button title to save goal editing"
)
String(localized: "overview.goal.save", defaultValue: "Save")
}
private static let numberFormatter: NumberFormatter = {
@@ -1405,19 +1200,9 @@ struct SystemOverviewView: View {
var shortLabel: String {
switch self {
case .voltage:
return NSLocalizedString(
"battery.bank.warning.voltage.short",
bundle: .main,
value: "Voltage",
comment: "Short label for voltage warning"
)
return String(localized: "battery.bank.warning.voltage.short", defaultValue: "Voltage")
case .capacity:
return NSLocalizedString(
"battery.bank.warning.capacity.short",
bundle: .main,
value: "Capacity",
comment: "Short label for capacity warning"
)
return String(localized: "battery.bank.warning.capacity.short", defaultValue: "Capacity")
}
}
}