Skip to content

Commit 494933e

Browse files
committed
Improve localization handling and fix placeholder text update issue
• Localization Enhancements: • Resolved the issue where the placeholder text in the TextField (“Search Location”) did not update when the app’s language changed at runtime. • Introduced a selectedLocale property in ContentViewState to manage locale changes manually. • Injected selectedLocale into the environment using .environment(\.locale, state.selectedLocale) to ensure views react to locale changes. • Added a language selection view (languageSelectionView) with options for English, Greek, and Albanian, allowing users to switch languages without restarting the app. • Updated the .id() modifier in locationTextField to use state.selectedLocale.identifier to force re-initialization when the locale changes. • Date Formatting Improvements: • Added a formattedDate(format:) method to the DayForecast model to format dates according to the user’s locale. • Updated the forecast view to display dates in the dd/MM format or according to the current locale settings. • Resolved Deprecation Warnings: • Updated the onChange modifiers to use the new closure signatures required in macOS 14.0 and iOS 17. • Changed single-parameter closures to accept two parameters (newValue and transaction) or zero parameters, as appropriate. • Localization Files Updated: • Ensured Localizable.strings files are properly set up with the correct keys and translations for English (en), Greek (el), and Albanian (sq). • Removed unused or redundant localization entries. • Updated localization keys in the code to match those in the Localizable.strings files. • Code Cleanup and Refactoring: • Removed redundant imports and comments for cleaner code. • Fixed typos and formatting inconsistencies in several files. • Updated WeatherManager to use a preferredLocale when reverse geocoding locations to ensure consistency. • Adjusted font sizes and layout in ForecastView for better UI alignment. • Improved the handling of optional values and error cases to enhance app stability. • Version Bump: • Updated the app version to 1.0.3 to reflect these changes.
1 parent 69dfc52 commit 494933e

File tree

7 files changed

+182
-65
lines changed

7 files changed

+182
-65
lines changed

Moti/Moti.xcodeproj/project.pbxproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@
218218
knownRegions = (
219219
en,
220220
Base,
221-
shq,
222221
el,
223222
sq,
224223
);

Moti/Moti/Models/WeatherData.swift

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ struct WeatherAPIResponse: Codable {
9090
}
9191

9292
struct DailyForecast: Codable, Identifiable {
93-
var id: UUID { UUID() } // Computed property to generate a new UUID each time
93+
var id: UUID { UUID() } // Computed property to generate a new UUID each time
9494
let date: String
9595
let date_epoch: Int
9696
let day: DayWeather
@@ -141,11 +141,17 @@ struct WeatherAPIResponse: Codable {
141141
moon_phase = try container.decode(String.self, forKey: .moon_phase)
142142

143143
// Try to decode moon_illumination as a String first, then as a Number if that fails
144-
if let illuminationString = try? container.decode(String.self, forKey: .moon_illumination) {
144+
if let illuminationString = try? container.decode(
145+
String.self, forKey: .moon_illumination)
146+
{
145147
moon_illumination = illuminationString
146-
} else if let illuminationNumber = try? container.decode(Int.self, forKey: .moon_illumination) {
148+
} else if let illuminationNumber = try? container.decode(
149+
Int.self, forKey: .moon_illumination)
150+
{
147151
moon_illumination = String(illuminationNumber)
148-
} else if let illuminationDouble = try? container.decode(Double.self, forKey: .moon_illumination) {
152+
} else if let illuminationDouble = try? container.decode(
153+
Double.self, forKey: .moon_illumination)
154+
{
149155
moon_illumination = String(illuminationDouble)
150156
} else {
151157
throw DecodingError.typeMismatch(
@@ -160,7 +166,7 @@ struct WeatherAPIResponse: Codable {
160166
}
161167

162168
struct HourlyForecast: Codable, Identifiable {
163-
var id: UUID { UUID() } // Computed property to generate a new UUID each time
169+
var id: UUID { UUID() } // Computed property to generate a new UUID each time
164170

165171
let time_epoch: Int
166172
let time: String
@@ -200,7 +206,7 @@ struct WeatherAPIResponse: Codable {
200206
}
201207

202208
struct Alerts: Codable {
203-
let alert: [Alert] // This can be expanded with more details about the alerts if needed
209+
let alert: [Alert] // This can be expanded with more details about the alerts if needed
204210
}
205211

206212
struct Alert: Codable {
@@ -210,7 +216,7 @@ struct WeatherAPIResponse: Codable {
210216

211217
// Updated DayForecast struct for use in SwiftUI views
212218
struct DayForecast: Codable, Identifiable {
213-
var id: UUID { UUID() } // Computed property to generate a new UUID each time
219+
var id: UUID { UUID() } // Computed property to generate a new UUID each time
214220
let date: String
215221
let weatherIcon: String
216222
let highTemp: Double
@@ -240,3 +246,22 @@ struct DayForecast: Codable, Identifiable {
240246
self.lowTemp = lowTemp
241247
}
242248
}
249+
250+
extension DayForecast {
251+
/// Formats the `date` string to `dd/MM` format.
252+
func formattedDate(format: String = "dd/MM") -> String {
253+
let inputFormatter = DateFormatter()
254+
inputFormatter.dateFormat = "yyyy-MM-dd" // Input date format from the API
255+
inputFormatter.locale = Locale(identifier: Locale.current.identifier)
256+
257+
let outputFormatter = DateFormatter()
258+
outputFormatter.dateFormat = format // Desired output format
259+
outputFormatter.locale = Locale.current
260+
261+
if let parsedDate = inputFormatter.date(from: date) {
262+
return outputFormatter.string(from: parsedDate)
263+
} else {
264+
return date // Return the original date if parsing fails
265+
}
266+
}
267+
}

Moti/Moti/Moti.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
373373
default:
374374
break
375375
}
376-
print("Current language set to: \(currentLanguage)") // Print the current language
376+
// print("Current language set to: \(currentLanguage)") // Print the current language
377377
updateLanguage()
378378
}
379379

Moti/Moti/Supporting Files/Localizable.xcstrings

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
"value" : "%.0f°C"
1010
}
1111
},
12+
"en" : {
13+
"stringUnit" : {
14+
"state" : "translated",
15+
"value" : "%.0f°C"
16+
}
17+
},
1218
"sq" : {
1319
"stringUnit" : {
1420
"state" : "translated",
@@ -17,112 +23,136 @@
1723
}
1824
}
1925
},
20-
"%lld-Day Forecast" : {
26+
"about" : {
27+
"extractionState" : "manual",
2128
"localizations" : {
2229
"el" : {
2330
"stringUnit" : {
2431
"state" : "translated",
25-
"value" : "%lld-Πρόγνωση Ημέρας"
32+
"value" : "Σχετικά"
33+
}
34+
},
35+
"en" : {
36+
"stringUnit" : {
37+
"state" : "translated",
38+
"value" : "About"
2639
}
2740
},
2841
"sq" : {
2942
"stringUnit" : {
3043
"state" : "translated",
31-
"value" : "%lld-Parashikimi I Ditës"
44+
"value" : "Rreth"
3245
}
3346
}
3447
}
3548
},
36-
"▲ %.0f°C" : {
49+
"change_language" : {
50+
"extractionState" : "manual",
3751
"localizations" : {
3852
"el" : {
3953
"stringUnit" : {
4054
"state" : "translated",
41-
"value" : "▲ %.0f°C"
55+
"value" : "Αλλαγή Γλώσσας"
56+
}
57+
},
58+
"en" : {
59+
"stringUnit" : {
60+
"state" : "translated",
61+
"value" : "Change Language"
4262
}
4363
},
4464
"sq" : {
4565
"stringUnit" : {
4666
"state" : "translated",
47-
"value" : "▲ %.0f°C"
67+
"value" : "Ndrysho Gjuhën"
4868
}
4969
}
5070
}
5171
},
52-
"▼ %.0f°C" : {
72+
"day_forecast" : {
5373
"localizations" : {
5474
"el" : {
5575
"stringUnit" : {
5676
"state" : "translated",
57-
"value" : "▼ %.0f°C"
77+
"value" : "Πρόβλεψη για τις επόμενες τρεις ημέρες"
78+
}
79+
},
80+
"en" : {
81+
"stringUnit" : {
82+
"state" : "translated",
83+
"value" : "Forecast for the next three days"
5884
}
5985
},
6086
"sq" : {
6187
"stringUnit" : {
6288
"state" : "translated",
63-
"value" : "▼ %.0f°C"
89+
"value" : "Parashikimi për tre ditët e ardhshme"
6490
}
6591
}
6692
}
6793
},
68-
"about" : {
69-
"extractionState" : "manual",
94+
"high_temp" : {
7095
"localizations" : {
7196
"el" : {
7297
"stringUnit" : {
7398
"state" : "translated",
74-
"value" : "Σχετικά"
99+
"value" : "▲ %d°C"
75100
}
76101
},
77102
"en" : {
78103
"stringUnit" : {
79104
"state" : "translated",
80-
"value" : "About"
105+
"value" : "▲ %d°C"
81106
}
82107
},
83108
"sq" : {
84109
"stringUnit" : {
85110
"state" : "translated",
86-
"value" : "Rreth"
111+
"value" : "▲ %d°C"
87112
}
88113
}
89114
}
90115
},
91-
"change_language" : {
92-
"extractionState" : "manual",
116+
"Location Access Denied" : {
93117
"localizations" : {
94118
"el" : {
95119
"stringUnit" : {
96120
"state" : "translated",
97-
"value" : "Αλλαγή Γλώσσας"
121+
"value" : "Άρνηση Πρόσβασης Τοποθεσίας"
98122
}
99123
},
100124
"en" : {
101125
"stringUnit" : {
102126
"state" : "translated",
103-
"value" : "Change Language"
127+
"value" : "Location Access Denied"
104128
}
105129
},
106130
"sq" : {
107131
"stringUnit" : {
108132
"state" : "translated",
109-
"value" : "Ndrysho Gjuhën"
133+
"value" : "Qasja në Vendndodhje u Refuzua"
110134
}
111135
}
112136
}
113137
},
114-
"Location Access Denied" : {
138+
"low_temp" : {
115139
"localizations" : {
116140
"el" : {
117141
"stringUnit" : {
118142
"state" : "translated",
119-
"value" : "Άρνηση Πρόσβασης Τοποθεσίας"
143+
"value" : "▼ %d°C"
144+
}
145+
},
146+
"en" : {
147+
"stringUnit" : {
148+
"state" : "translated",
149+
"value" : "▼ %d°C"
120150
}
121151
},
122152
"sq" : {
123153
"stringUnit" : {
124154
"state" : "translated",
125-
"value" : "Qasja në Vendndodhje u Refuzua"
155+
"value" : "▼ %d°C"
126156
}
127157
}
128158
}
@@ -135,6 +165,12 @@
135165
"value" : "Ανοίξτε τις Προτιμήσεις Συστήματος"
136166
}
137167
},
168+
"en" : {
169+
"stringUnit" : {
170+
"state" : "translated",
171+
"value" : "Open System Preferences"
172+
}
173+
},
138174
"sq" : {
139175
"stringUnit" : {
140176
"state" : "translated",
@@ -151,6 +187,12 @@
151187
"value" : "Ενεργοποιήστε τις υπηρεσίες τοποθεσίας στις Προτιμήσεις Συστήματος για να λαμβάνετε ενημερώσεις για τον καιρό."
152188
}
153189
},
190+
"en" : {
191+
"stringUnit" : {
192+
"state" : "translated",
193+
"value" : "Please enable location services in System Preferences to get weather updates."
194+
}
195+
},
154196
"sq" : {
155197
"stringUnit" : {
156198
"state" : "translated",
@@ -208,15 +250,21 @@
208250
"Search Location" : {
209251
"localizations" : {
210252
"el" : {
253+
"stringUnit" : {
254+
"state" : "needs_review",
255+
"value" : "🔍"
256+
}
257+
},
258+
"en" : {
211259
"stringUnit" : {
212260
"state" : "translated",
213-
"value" : "Αναζήτηση Τοποθεσίας"
261+
"value" : "🔍"
214262
}
215263
},
216264
"sq" : {
217265
"stringUnit" : {
218-
"state" : "translated",
219-
"value" : "Kërko Vendndodhja"
266+
"state" : "needs_review",
267+
"value" : "🔍"
220268
}
221269
}
222270
}

Moti/Moti/ViewModels/WeatherManager.swift

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,29 @@ class WeatherManager: NSObject, ObservableObject, CLLocationManagerDelegate {
216216
self.currentTemperature = decodedData.current.temp_c
217217
self.currentWeatherIcon = "https:" + decodedData.current.condition.icon
218218
self.forecast = decodedData.forecast.forecastday.map { forecastDay in
219-
DayForecast(
220-
date: Date(timeIntervalSince1970: TimeInterval(forecastDay.date_epoch))
221-
.formatted(.dateTime.weekday()),
222-
weatherIcon: "https:" + forecastDay.day.condition.icon,
223-
highTemp: forecastDay.day.maxtemp_c,
224-
lowTemp: forecastDay.day.mintemp_c
225-
)
219+
// Use DateFormatter to format the date as dd/MM
220+
let inputFormatter = DateFormatter()
221+
inputFormatter.dateFormat = "yyyy-MM-dd"
222+
let outputFormatter = DateFormatter()
223+
outputFormatter.dateFormat = "dd/MM"
224+
225+
if let parsedDate = inputFormatter.date(from: forecastDay.date) {
226+
let formattedDate = outputFormatter.string(from: parsedDate)
227+
return DayForecast(
228+
date: formattedDate,
229+
weatherIcon: "https:" + forecastDay.day.condition.icon,
230+
highTemp: forecastDay.day.maxtemp_c,
231+
lowTemp: forecastDay.day.mintemp_c
232+
)
233+
} else {
234+
// Fallback to original date string if parsing fails
235+
return DayForecast(
236+
date: forecastDay.date,
237+
weatherIcon: "https:" + forecastDay.day.condition.icon,
238+
highTemp: forecastDay.day.maxtemp_c,
239+
lowTemp: forecastDay.day.mintemp_c
240+
)
241+
}
226242
}
227243
}
228244
} catch {
@@ -231,7 +247,11 @@ class WeatherManager: NSObject, ObservableObject, CLLocationManagerDelegate {
231247
}
232248

233249
private func reverseGeocode(location: CLLocation) {
234-
CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
250+
let geocoder = CLGeocoder()
251+
let preferredLocale = Locale(identifier: "en_US")
252+
253+
geocoder.reverseGeocodeLocation(location, preferredLocale: preferredLocale) {
254+
placemarks, error in
235255
if let error = error {
236256
print("Reverse geocoding failed: \(error.localizedDescription)")
237257
return

0 commit comments

Comments
 (0)