Localize alerts and favourites copy
This commit is contained in:
+31
-31
@@ -38,7 +38,7 @@ struct AlertsView: View {
|
|||||||
/// "5 miles" or "8 km" — for the "Follow search" rows.
|
/// "5 miles" or "8 km" — for the "Follow search" rows.
|
||||||
private var searchInUnitLabel: String {
|
private var searchInUnitLabel: String {
|
||||||
let shown = distanceUnit.displayMiles(stationLimit) // stationLimit is always miles
|
let shown = distanceUnit.displayMiles(stationLimit) // stationLimit is always miles
|
||||||
return "\(shown) \(distanceUnit.label(for: Double(shown)))"
|
return String(format: String(localized: "%lld %@", comment: "Distance label"), shown, distanceUnit.label(for: Double(shown)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A miles value (5/10/15) shown truthfully in the user's unit, e.g.
|
/// A miles value (5/10/15) shown truthfully in the user's unit, e.g.
|
||||||
@@ -46,7 +46,7 @@ struct AlertsView: View {
|
|||||||
/// what the option really means.
|
/// what the option really means.
|
||||||
private func distanceLabel(forMiles miles: Int) -> String {
|
private func distanceLabel(forMiles miles: Int) -> String {
|
||||||
let shown = distanceUnit.displayMiles(miles)
|
let shown = distanceUnit.displayMiles(miles)
|
||||||
return "\(shown) \(distanceUnit.label(for: Double(shown)))"
|
return String(format: String(localized: "%lld %@", comment: "Distance label"), shown, distanceUnit.label(for: Double(shown)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Situational guidance for the currently-selected alert radius, shown
|
/// Situational guidance for the currently-selected alert radius, shown
|
||||||
@@ -57,23 +57,23 @@ struct AlertsView: View {
|
|||||||
private var alertRadiusGuidance: String {
|
private var alertRadiusGuidance: String {
|
||||||
switch alertChoice.wrappedValue {
|
switch alertChoice.wrappedValue {
|
||||||
case .followSearch:
|
case .followSearch:
|
||||||
return "Set-and-forget: alerts behave exactly like the Stations-tab search distance (capped at 8 miles for reliable geofencing)."
|
return String(localized: "Set-and-forget: alerts behave exactly like the Stations-tab search distance (capped at 8 miles for reliable geofencing).", comment: "Alert radius guidance for following search distance")
|
||||||
case .fixed(let value):
|
case .fixed(let value):
|
||||||
let optionLabel = "\(value) \(distanceUnit.label(for: Double(value)))"
|
let optionLabel = String(format: String(localized: "%lld %@", comment: "Distance label"), value, distanceUnit.label(for: Double(value)))
|
||||||
let body: String
|
let body: String
|
||||||
switch value {
|
switch value {
|
||||||
case 1:
|
case 1:
|
||||||
body = "Town driving. Fires most often — you're rarely inside the circle for long, so pings are meaningful. Best for stop-start local trips."
|
body = String(localized: "Town driving. Fires most often — you're rarely inside the circle for long, so pings are meaningful. Best for stop-start local trips.", comment: "Alert radius guidance")
|
||||||
case 2:
|
case 2:
|
||||||
body = "City or suburb commute. Slightly earlier heads-up than the smallest option; still fires regularly around town."
|
body = String(localized: "City or suburb commute. Slightly earlier heads-up than the smallest option; still fires regularly around town.", comment: "Alert radius guidance")
|
||||||
case 3:
|
case 3:
|
||||||
body = "Mixed driving. Middle ground: heads-up a few minutes out, still works in most towns."
|
body = String(localized: "Mixed driving. Middle ground: heads-up a few minutes out, still works in most towns.", comment: "Alert radius guidance")
|
||||||
case 5:
|
case 5:
|
||||||
body = "A-roads and motorway exits. Wide early warning for longer trips — but in town it rarely fires, because you're usually already inside the circle."
|
body = String(localized: "A-roads and motorway exits. Wide early warning for longer trips — but in town it rarely fires, because you're usually already inside the circle.", comment: "Alert radius guidance")
|
||||||
default:
|
default:
|
||||||
body = "Long-distance journeys. Maximum reach: a ping when a cheap station is coming up from far out. In town this essentially never fires."
|
body = String(localized: "Long-distance journeys. Maximum reach: a ping when a cheap station is coming up from far out. In town this essentially never fires.", comment: "Alert radius guidance")
|
||||||
}
|
}
|
||||||
return "\(optionLabel) — \(body)"
|
return String(format: String(localized: "%@ — %@", comment: "Alert radius guidance with option label"), optionLabel, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,74 +134,74 @@ struct AlertsView: View {
|
|||||||
NavigationStack {
|
NavigationStack {
|
||||||
List {
|
List {
|
||||||
Section {
|
Section {
|
||||||
Toggle("Cheapest favourite", isOn: $favouriteDropEnabled)
|
Toggle(String(localized: "Cheapest favourite", comment: "Favourite price-drop alert toggle"), isOn: $favouriteDropEnabled)
|
||||||
Picker("Fuel", selection: $favouriteDropFuel) {
|
Picker(String(localized: "Fuel", comment: "Fuel picker title"), selection: $favouriteDropFuel) {
|
||||||
ForEach(FuelType.allCases) { fuel in
|
ForEach(FuelType.allCases) { fuel in
|
||||||
Text(fuel.displayName).tag(fuel)
|
Text(fuel.displayName).tag(fuel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button("Check now", action: onCheckFavouriteDropNow)
|
Button(String(localized: "Check now", comment: "Manual favourite price-drop check button"), action: onCheckFavouriteDropNow)
|
||||||
.disabled(!favouriteDropEnabled)
|
.disabled(!favouriteDropEnabled)
|
||||||
} header: {
|
} header: {
|
||||||
Text("Price-drop alerts")
|
Text(String(localized: "Price-drop alerts", comment: "Favourite price-drop alerts section title"))
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Watches your cheapest saved favourite for the fuel you pick here. FuelBoard alerts when a different favourite becomes the cheapest, or when your current cheapest favourite drops by at least 1.0p. This is separate from the nearby-geofence alert, so you can monitor favourites without extra location noise.")
|
Text(String(localized: "Watches your cheapest saved favourite for the fuel you pick here. FuelBoard alerts when a different favourite becomes the cheapest, or when your current cheapest favourite drops by at least 1.0p. This is separate from the nearby-geofence alert, so you can monitor favourites without extra location noise.", comment: "Favourite price-drop alerts section footer"))
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Toggle("Cheapest station", isOn: $enabled)
|
Toggle(String(localized: "Cheapest station", comment: "Proximity alert toggle"), isOn: $enabled)
|
||||||
Picker("Fuel", selection: $fuel) {
|
Picker(String(localized: "Fuel", comment: "Fuel picker title"), selection: $fuel) {
|
||||||
ForEach(FuelType.allCases) { fuel in
|
ForEach(FuelType.allCases) { fuel in
|
||||||
Text(fuel.displayName).tag(fuel)
|
Text(fuel.displayName).tag(fuel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Picker("Radius", selection: alertChoice) {
|
Picker(String(localized: "Radius", comment: "Alert radius picker title"), selection: alertChoice) {
|
||||||
Text("Follow search (\(searchInUnitLabel))").tag(AlertRadiusChoice.followSearch)
|
Text(String(format: String(localized: "Follow search (%@)", comment: "Follow current station search distance option"), searchInUnitLabel)).tag(AlertRadiusChoice.followSearch)
|
||||||
ForEach(FuelStore.alertRadiusOptions, id: \.self) { value in
|
ForEach(FuelStore.alertRadiusOptions, id: \.self) { value in
|
||||||
Text("\(value) \(distanceUnit.label(for: Double(value)))").tag(AlertRadiusChoice.fixed(value))
|
Text(String(format: String(localized: "%lld %@", comment: "Distance label"), value, distanceUnit.label(for: Double(value)))).tag(AlertRadiusChoice.fixed(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text(alertRadiusGuidance)
|
Text(alertRadiusGuidance)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
if enabled && monitoredCount == 0 {
|
if enabled && monitoredCount == 0 {
|
||||||
Text("No stations monitored yet — open the Stations tab to load prices first.")
|
Text(String(localized: "No stations monitored yet — open the Stations tab to load prices first.", comment: "No monitored stations hint"))
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Proximity alerts")
|
Text(String(localized: "Proximity alerts", comment: "Proximity alerts section title"))
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("When you approach a station that is the cheapest within the radius, FuelBoard sends a notification — even with the app closed. Nearby stations selling \(fuel.displayName.lowercased()) are watched — favourites get priority — and each station alerts at most once per hour. Follow search mirrors the Stations-tab distance, capped at 8 miles for reliable geofencing. Smaller radii ping more often but later; larger radii ping rarely in town but earlier from afar.")
|
Text(String(format: String(localized: "When you approach a station that is the cheapest within the radius, FuelBoard sends a notification — even with the app closed. Nearby stations selling %@ are watched — favourites get priority — and each station alerts at most once per hour. Follow search mirrors the Stations-tab distance, capped at 8 miles for reliable geofencing. Smaller radii ping more often but later; larger radii ping rarely in town but earlier from afar.", comment: "Proximity alerts section footer"), fuel.displayName.lowercased()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if enabled, let lastAlert {
|
if enabled, let lastAlert {
|
||||||
Section("Last alert") {
|
Section(String(localized: "Last alert", comment: "Last alert section title")) {
|
||||||
Label(lastAlert, systemImage: "bell.badge.fill")
|
Label(lastAlert, systemImage: "bell.badge.fill")
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Toggle("Cheapest station", isOn: $liveActivityEnabled)
|
Toggle(String(localized: "Cheapest station", comment: "Live Activity toggle"), isOn: $liveActivityEnabled)
|
||||||
Picker("Fuel", selection: $liveActivityFuel) {
|
Picker(String(localized: "Fuel", comment: "Fuel picker title"), selection: $liveActivityFuel) {
|
||||||
ForEach(FuelType.allCases) { fuel in
|
ForEach(FuelType.allCases) { fuel in
|
||||||
Text(fuel.displayName).tag(fuel)
|
Text(fuel.displayName).tag(fuel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Picker("Distance", selection: laChoice) {
|
Picker(String(localized: "Distance", comment: "Distance picker title"), selection: laChoice) {
|
||||||
Text("Follow search (\(searchInUnitLabel))").tag(LADistanceChoice.followSearch)
|
Text(String(format: String(localized: "Follow search (%@)", comment: "Follow current station search distance option"), searchInUnitLabel)).tag(LADistanceChoice.followSearch)
|
||||||
ForEach(FuelStore.stationRadiusOptions, id: \.self) { miles in
|
ForEach(FuelStore.stationRadiusOptions, id: \.self) { miles in
|
||||||
Text(distanceLabel(forMiles: miles)).tag(LADistanceChoice.fixed(miles))
|
Text(distanceLabel(forMiles: miles)).tag(LADistanceChoice.fixed(miles))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Live Activity")
|
Text(String(localized: "Live Activity", comment: "Live Activity section title"))
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Shows the cheapest station for the fuel and distance you pick here on the Lock Screen and Dynamic Island — independent of the Stations tab (choose Follow search to mirror its distance instead). Updates as you drive; tap to open directions. Live Activities can be disabled in System Settings → Live Activities.")
|
Text(String(localized: "Shows the cheapest station for the fuel and distance you pick here on the Lock Screen and Dynamic Island — independent of the Stations tab (choose Follow search to mirror its distance instead). Updates as you drive; tap to open directions. Live Activities can be disabled in System Settings → Live Activities.", comment: "Live Activity section footer"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Alerts")
|
.navigationTitle(String(localized: "Alerts", comment: "Alerts tab title"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,9 +106,9 @@ struct FavouritesView: View {
|
|||||||
Image(systemName: "star")
|
Image(systemName: "star")
|
||||||
.font(.system(size: 40))
|
.font(.system(size: 40))
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
Text("No favourites yet")
|
Text(String(localized: "No favourites yet", comment: "Favourites empty-state title"))
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
Text("Tap the star on any station in the Stations tab to pin it here. The first station in each list becomes the favourite used in single widgets — drag to put your pick first.")
|
Text(String(localized: "Tap the star on any station in the Stations tab to pin it here. The first station in each list becomes the favourite used in single widgets — drag to put your pick first.", comment: "Favourites empty-state explanation"))
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
@@ -117,7 +117,7 @@ struct FavouritesView: View {
|
|||||||
.padding(.vertical, 24)
|
.padding(.vertical, 24)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Section("Fuel type") {
|
Section(String(localized: "Fuel type", comment: "Fuel type section title")) {
|
||||||
FuelTypeSegmentedPicker(
|
FuelTypeSegmentedPicker(
|
||||||
selection: $fuel,
|
selection: $fuel,
|
||||||
fuels: availableFuels
|
fuels: availableFuels
|
||||||
@@ -142,21 +142,21 @@ struct FavouritesView: View {
|
|||||||
}
|
}
|
||||||
.onMove(perform: moveFavourite)
|
.onMove(perform: moveFavourite)
|
||||||
} header: {
|
} header: {
|
||||||
Text("\(activeFuel.displayName) favourites")
|
Text(String(format: String(localized: "%@ favourites", comment: "Favourites section title for fuel type"), activeFuel.displayName))
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("The first station is the favourite used in single widgets. Tap Edit, then drag to reorder.")
|
Text(String(localized: "The first station is the favourite used in single widgets. Tap Edit, then drag to reorder.", comment: "Favourites reorder explanation"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let cheapest = cheapest {
|
if let cheapest = cheapest {
|
||||||
Section {
|
Section {
|
||||||
Text("\(cheapest.name) is your cheapest \(activeFuel.displayName) favourite. Favourites are monitored automatically — switch to the Alerts tab and enable alerts to get pinged when you approach one that's the cheapest within the radius.")
|
Text(String(format: String(localized: "%@ is your cheapest %@ favourite. Favourites are monitored automatically — switch to the Alerts tab and enable alerts to get pinged when you approach one that's the cheapest within the radius.", comment: "Cheapest favourite explanation"), cheapest.name, activeFuel.displayName))
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Favourites")
|
.navigationTitle(String(localized: "Favourites", comment: "Favourites tab title"))
|
||||||
.toolbar {
|
.toolbar {
|
||||||
if !favourites.isEmpty {
|
if !favourites.isEmpty {
|
||||||
ToolbarItemGroup(placement: .topBarTrailing) {
|
ToolbarItemGroup(placement: .topBarTrailing) {
|
||||||
@@ -164,7 +164,7 @@ struct FavouritesView: View {
|
|||||||
showTrends = true
|
showTrends = true
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "chart.xyaxis.line")
|
Image(systemName: "chart.xyaxis.line")
|
||||||
.accessibilityLabel("Trends")
|
.accessibilityLabel(String(localized: "Trends", comment: "Trends button accessibility label"))
|
||||||
}
|
}
|
||||||
EditButton()
|
EditButton()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,3 +182,19 @@
|
|||||||
"Check your internet connection" = "Check your internet connection";
|
"Check your internet connection" = "Check your internet connection";
|
||||||
"Tap to try again" = "Tap to try again";
|
"Tap to try again" = "Tap to try again";
|
||||||
"Check your internet connection. Tap to try again" = "Check your internet connection. Tap to try again";
|
"Check your internet connection. Tap to try again" = "Check your internet connection. Tap to try again";
|
||||||
|
|
||||||
|
/* Alerts and favourites localisation pass */
|
||||||
|
"Cheapest favourite" = "Cheapest favourite";
|
||||||
|
"Check now" = "Check now";
|
||||||
|
"Price-drop alerts" = "Price-drop alerts";
|
||||||
|
"Watches your cheapest saved favourite for the fuel you pick here. FuelBoard alerts when a different favourite becomes the cheapest, or when your current cheapest favourite drops by at least 1.0p. This is separate from the nearby-geofence alert, so you can monitor favourites without extra location noise." = "Watches your cheapest saved favourite for the fuel you pick here. FuelBoard alerts when a different favourite becomes the cheapest, or when your current cheapest favourite drops by at least 1.0p. This is separate from the nearby-geofence alert, so you can monitor favourites without extra location noise.";
|
||||||
|
"Cheapest station" = "Cheapest station";
|
||||||
|
"Proximity alerts" = "Proximity alerts";
|
||||||
|
"When you approach a station that is the cheapest within the radius, FuelBoard sends a notification — even with the app closed. Nearby stations selling %@ are watched — favourites get priority — and each station alerts at most once per hour. Follow search mirrors the Stations-tab distance, capped at 8 miles for reliable geofencing. Smaller radii ping more often but later; larger radii ping rarely in town but earlier from afar." = "When you approach a station that is the cheapest within the radius, FuelBoard sends a notification — even with the app closed. Nearby stations selling %@ are watched — favourites get priority — and each station alerts at most once per hour. Follow search mirrors the Stations-tab distance, capped at 8 miles for reliable geofencing. Smaller radii ping more often but later; larger radii ping rarely in town but earlier from afar.";
|
||||||
|
"Set-and-forget: alerts behave exactly like the Stations-tab search distance (capped at 8 miles for reliable geofencing)." = "Set-and-forget: alerts behave exactly like the Stations-tab search distance (capped at 8 miles for reliable geofencing).";
|
||||||
|
"Town driving. Fires most often — you're rarely inside the circle for long, so pings are meaningful. Best for stop-start local trips." = "Town driving. Fires most often — you're rarely inside the circle for long, so pings are meaningful. Best for stop-start local trips.";
|
||||||
|
"City or suburb commute. Slightly earlier heads-up than the smallest option; still fires regularly around town." = "City or suburb commute. Slightly earlier heads-up than the smallest option; still fires regularly around town.";
|
||||||
|
"Mixed driving. Middle ground: heads-up a few minutes out, still works in most towns." = "Mixed driving. Middle ground: heads-up a few minutes out, still works in most towns.";
|
||||||
|
"A-roads and motorway exits. Wide early warning for longer trips — but in town it rarely fires, because you're usually already inside the circle." = "A-roads and motorway exits. Wide early warning for longer trips — but in town it rarely fires, because you're usually already inside the circle.";
|
||||||
|
"Long-distance journeys. Maximum reach: a ping when a cheap station is coming up from far out. In town this essentially never fires." = "Long-distance journeys. Maximum reach: a ping when a cheap station is coming up from far out. In town this essentially never fires.";
|
||||||
|
"%@ — %@" = "%@ — %@";
|
||||||
|
|||||||
Reference in New Issue
Block a user