From 1cf4ff21ecb368b27265c379b4e7d5515001d538 Mon Sep 17 00:00:00 2001 From: Yannik Bloscheck Date: Thu, 24 Jul 2025 21:32:04 +0200 Subject: [PATCH] [ios] Added setting for map language Signed-off-by: Yannik Bloscheck --- iphone/Maps/Core/Settings/MWMSettings.h | 4 ++ iphone/Maps/Core/Settings/MWMSettings.mm | 32 ++++++++++++++++ .../en-GB.lproj/Localizable.strings | 7 ++++ .../en.lproj/Localizable.strings | 7 ++++ iphone/Maps/Maps.xcodeproj/project.pbxproj | 8 ++++ .../Maps/Model/Settings Types/Language.swift | 31 +++++++++++++++ .../Model/Settings Types/MapLanguage.swift | 13 +++++++ .../Settings Types/VoiceRoutingLanguage.swift | 24 ++---------- iphone/Maps/Model/Settings.swift | 22 +++++++++++ iphone/Maps/UI/Settings/SettingsView.swift | 38 ++++++++++++++++++- map/framework.cpp | 10 +++++ map/framework.hpp | 1 + 12 files changed, 174 insertions(+), 23 deletions(-) create mode 100644 iphone/Maps/Model/Settings Types/Language.swift create mode 100644 iphone/Maps/Model/Settings Types/MapLanguage.swift diff --git a/iphone/Maps/Core/Settings/MWMSettings.h b/iphone/Maps/Core/Settings/MWMSettings.h index 49978d655..29e255b31 100644 --- a/iphone/Maps/Core/Settings/MWMSettings.h +++ b/iphone/Maps/Core/Settings/MWMSettings.h @@ -37,6 +37,10 @@ NS_SWIFT_NAME(SettingsBridge) + (BOOL)largeFontSize; + (void)setLargeFontSize:(BOOL)largeFontSize; ++ (NSDictionary *)availableMapLanguages; ++ (NSString *)mapLanguageCode; ++ (void)setMapLanguageCode:(NSString *)mapLanguageCode; + + (BOOL)transliteration; + (void)setTransliteration:(BOOL)transliteration; diff --git a/iphone/Maps/Core/Settings/MWMSettings.mm b/iphone/Maps/Core/Settings/MWMSettings.mm index 297cb7a8a..dd6b6d379 100644 --- a/iphone/Maps/Core/Settings/MWMSettings.mm +++ b/iphone/Maps/Core/Settings/MWMSettings.mm @@ -13,6 +13,7 @@ namespace char const * kAutoDownloadEnabledKey = "AutoDownloadEnabled"; char const * kZoomButtonsEnabledKey = "ZoomButtonsEnabled"; char const * kCompassCalibrationEnabledKey = "CompassCalibrationEnabled"; +char const * kMapLanguageCode = "MapLanguageCode"; char const * kRoutingDisclaimerApprovedKey = "IsDisclaimerApproved"; // TODO(igrechuhin): Remove outdated kUDAutoNightModeOff @@ -202,6 +203,37 @@ NSString * const kUDFileLoggingEnabledKey = @"FileLoggingEnabledKey"; GetFramework().SetLargeFontsSize(static_cast(largeFontSize)); } ++ (NSDictionary *)availableMapLanguages; +{ + NSMutableDictionary *availableLanguages = [[NSMutableDictionary alloc] init]; + auto const & v = StringUtf8Multilang::GetSupportedLanguages(false); + for (auto i: v) { + [availableLanguages setObject:@(std::string(i.m_name).c_str()) forKey:@(std::string(i.m_code).c_str())]; + } + return availableLanguages; +} + ++ (NSString *)mapLanguageCode; +{ + std::string mapLanguageCode; + bool hasMapLanguageCode = settings::Get(kMapLanguageCode, mapLanguageCode); + if (hasMapLanguageCode) { + return @(mapLanguageCode.c_str()); + } + + return @"auto"; +} + ++ (void)setMapLanguageCode:(NSString *)mapLanguageCode; +{ + auto &f = GetFramework(); + if ([mapLanguageCode isEqual: @"auto"]) { + f.ResetMapLanguageCode(); + } else { + f.SetMapLanguageCode(std::string([mapLanguageCode UTF8String])); + } +} + + (BOOL)transliteration { return GetFramework().LoadTransliteration(); } + (void)setTransliteration:(BOOL)transliteration { diff --git a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings index 91bb86e4e..79a2d3ee0 100644 --- a/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/en-GB.lproj/Localizable.strings @@ -209,6 +209,12 @@ "pref_zoom_title" = "Zoom buttons"; "pref_left_button_type" = "Left Main Button"; +/* Settings «Map» category: «Map Language» title */ +"pref_maplanguage_title" = "Map Language"; + +/* Settings «Map» category: «Map Language» local value */ +"pref_maplanguage_local" = "Local Language"; + /* Settings «Map» category: «Map Appearance» title */ "pref_mapappearance_title" = "Map Appearance"; @@ -746,6 +752,7 @@ "enable_logging" = "Enable logging"; "log_file_size" = "Log file size: %@"; "transliteration_title" = "Transliterate into Latin alphabet"; +"transliteration_title_disabled_summary" = "Disabled when always using the local language for the map"; /* Subway exits for public transport marks on the map */ "core_exit" = "Exit"; diff --git a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings index 36cf58aa1..ace724220 100644 --- a/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings +++ b/iphone/Maps/LocalizedStrings/en.lproj/Localizable.strings @@ -219,6 +219,12 @@ "pref_zoom_title" = "Zoom buttons"; "pref_left_button_type" = "Left Main Button"; +/* Settings «Map» category: «Map Language» title */ +"pref_maplanguage_title" = "Map Language"; + +/* Settings «Map» category: «Map Language» local value */ +"pref_maplanguage_local" = "Local Language"; + /* Settings «Map» category: «Map Appearance» title */ "pref_mapappearance_title" = "Map Appearance"; @@ -764,6 +770,7 @@ "enable_logging" = "Enable logging"; "log_file_size" = "Log file size: %@"; "transliteration_title" = "Transliterate into Latin alphabet"; +"transliteration_title_disabled_summary" = "Disabled when always using the local language for the map"; /* Subway exits for public transport marks on the map */ "core_exit" = "Exit"; diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index daad8192f..2ba6fbbbc 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 272F1F3B2E0EE0A300FA52EF /* NoExistingProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1F3A2E0EE09500FA52EF /* NoExistingProfileView.swift */; }; 272F1F3D2E0EE0C800FA52EF /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1F3C2E0EE0C400FA52EF /* ProfileView.swift */; }; 272F1F462E0EEF9400FA52EF /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 272F1F452E0EEF8B00FA52EF /* SafariView.swift */; }; + 2752B6CA2E31197500887CC4 /* MapLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2752B6C92E31197000887CC4 /* MapLanguage.swift */; }; + 2752B6CE2E3121D900887CC4 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2752B6CD2E3121D800887CC4 /* Language.swift */; }; 2765D1D02E13F9C20005CA2B /* BridgeControllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2765D1CD2E13F9BC0005CA2B /* BridgeControllers.swift */; }; 27697F742E25177600FBD913 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27697F732E25177300FBD913 /* AboutView.swift */; }; 27697F7F2E254A5500FBD913 /* CopyrightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27697F7C2E254A5000FBD913 /* CopyrightView.swift */; }; @@ -774,6 +776,8 @@ 272F1F3A2E0EE09500FA52EF /* NoExistingProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoExistingProfileView.swift; sourceTree = ""; }; 272F1F3C2E0EE0C400FA52EF /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; 272F1F452E0EEF8B00FA52EF /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; }; + 2752B6C92E31197000887CC4 /* MapLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLanguage.swift; sourceTree = ""; }; + 2752B6CD2E3121D800887CC4 /* Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = ""; }; 2765D1CD2E13F9BC0005CA2B /* BridgeControllers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgeControllers.swift; sourceTree = ""; }; 27697F732E25177300FBD913 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; 27697F7C2E254A5000FBD913 /* CopyrightView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyrightView.swift; sourceTree = ""; }; @@ -1981,6 +1985,8 @@ 27AF185B2E1DB64B00CD41E2 /* Settings Types */ = { isa = PBXGroup; children = ( + 2752B6CD2E3121D800887CC4 /* Language.swift */, + 2752B6C92E31197000887CC4 /* MapLanguage.swift */, 27AF184F2E1DB61500CD41E2 /* VoiceRoutingLanguage.swift */, 27768FDF2E201BE60086784A /* LeftButtonType.swift */, 27AF18512E1DB61F00CD41E2 /* DistanceUnit.swift */, @@ -4463,6 +4469,7 @@ 3454D7C51E07F045004AF2AD /* UIButton+Orientation.m in Sources */, 34AB66831FC5AA330078E451 /* NavigationAddPointToastView.swift in Sources */, F6E2FE4C1E097BA00083EBEC /* MWMPlacePageManager.mm in Sources */, + 2752B6CA2E31197500887CC4 /* MapLanguage.swift in Sources */, 3404757E1E081B3300C92850 /* iosOGLContext.mm in Sources */, 993F5513237C622700545511 /* DeepLinkHandler.swift in Sources */, 993DF11223F6BDB100AC231A /* UIImageRenderer.swift in Sources */, @@ -4589,6 +4596,7 @@ 6741A9E71BF340DE002C974C /* MWMCircularProgressView.m in Sources */, 34AC8FDB1EFC07FE00E7F910 /* UILabel+NumberOfVisibleLines.swift in Sources */, ED79A5AD2BD7BA0F00952D1F /* UIApplication+LoadingOverlay.swift in Sources */, + 2752B6CE2E3121D900887CC4 /* Language.swift in Sources */, 9959C75C24599CCD008FD4FD /* DirectionView.swift in Sources */, 47CA68D62500448D00671019 /* BookmarksListInteractor.swift in Sources */, 4767CD9F20AAD48A00BD8166 /* Checkmark.swift in Sources */, diff --git a/iphone/Maps/Model/Settings Types/Language.swift b/iphone/Maps/Model/Settings Types/Language.swift new file mode 100644 index 000000000..f4e4aafb0 --- /dev/null +++ b/iphone/Maps/Model/Settings Types/Language.swift @@ -0,0 +1,31 @@ +extension Settings { + /// A language + protocol Language: Codable, Identifiable, Equatable, Comparable { + // MARK: Properties + + /// The id + var id: String { get } + + + /// The localized name + var localizedName: String { get } + } +} + + + +// MARK: - Comparable +extension Settings.Language { + static func == (lhs: Self, rhs: Self) -> Bool { + return lhs.id == rhs.id + } +} + + + +// MARK: - Comparable +extension Settings.Language { + static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.localizedName.localizedCaseInsensitiveCompare(rhs.localizedName) == .orderedAscending + } +} diff --git a/iphone/Maps/Model/Settings Types/MapLanguage.swift b/iphone/Maps/Model/Settings Types/MapLanguage.swift new file mode 100644 index 000000000..db2987e4a --- /dev/null +++ b/iphone/Maps/Model/Settings Types/MapLanguage.swift @@ -0,0 +1,13 @@ +extension Settings { + /// A language used for the map + struct MapLanguage: Language { + // MARK: Properties + + /// The id + let id: String + + + /// The localized name + let localizedName: String + } +} diff --git a/iphone/Maps/Model/Settings Types/VoiceRoutingLanguage.swift b/iphone/Maps/Model/Settings Types/VoiceRoutingLanguage.swift index e3e4e354e..f21162afe 100644 --- a/iphone/Maps/Model/Settings Types/VoiceRoutingLanguage.swift +++ b/iphone/Maps/Model/Settings Types/VoiceRoutingLanguage.swift @@ -1,31 +1,13 @@ extension Settings { /// A language used for voice guidance during routing - struct VoiceRoutingLanguage: Codable, Identifiable { + struct VoiceRoutingLanguage: Language { // MARK: Properties /// The id - var id: String + let id: String /// The localized name - var localizedName: String - } -} - - - -// MARK: - Comparable -extension Settings.VoiceRoutingLanguage: Equatable { - static func == (lhs: Settings.VoiceRoutingLanguage, rhs: Settings.VoiceRoutingLanguage) -> Bool { - return lhs.id == rhs.id - } -} - - - -// MARK: - Comparable -extension Settings.VoiceRoutingLanguage: Comparable { - static func < (lhs: Settings.VoiceRoutingLanguage, rhs: Settings.VoiceRoutingLanguage) -> Bool { - return lhs.localizedName.localizedCaseInsensitiveCompare(rhs.localizedName) == .orderedAscending + let localizedName: String } } diff --git a/iphone/Maps/Model/Settings.swift b/iphone/Maps/Model/Settings.swift index caa36e0b7..d04623fb2 100644 --- a/iphone/Maps/Model/Settings.swift +++ b/iphone/Maps/Model/Settings.swift @@ -140,6 +140,28 @@ import Combine } + /// The available languages for the map + static var availableLanguagesForMap: [MapLanguage] { + var languages = SettingsBridge.availableMapLanguages().map { language in + return MapLanguage(id: language.key, localizedName: language.value) + }.sorted() + languages.insert(MapLanguage(id: "default", localizedName: String(localized: "pref_maplanguage_local")), at: 0) + languages.insert(MapLanguage(id: "auto", localizedName: String(localized: "auto")), at: 0) + return languages + } + + + /// The current language for the map + static var languageForMap: MapLanguage.ID { + get { + return SettingsBridge.mapLanguageCode() + } + set { + SettingsBridge.setMapLanguageCode(newValue) + } + } + + /// If the compass should be calibrated @objc static var shouldCalibrateCompass: Bool { get { diff --git a/iphone/Maps/UI/Settings/SettingsView.swift b/iphone/Maps/UI/Settings/SettingsView.swift index 39cc74e9a..c2f1e65d8 100644 --- a/iphone/Maps/UI/Settings/SettingsView.swift +++ b/iphone/Maps/UI/Settings/SettingsView.swift @@ -32,6 +32,10 @@ struct SettingsView: View { @State private var hasIncreasedFontsize: Bool = false + /// The selected language for the map + @State var selectedLanguageForMap: Settings.MapLanguage.ID? = nil + + /// If names should be transliterated to Latin @State private var shouldTransliterateToLatin: Bool = true @@ -136,8 +140,32 @@ struct SettingsView: View { Toggle("big_font", isOn: $hasIncreasedFontsize) .tint(.accent) - Toggle("transliteration_title", isOn: $shouldTransliterateToLatin) - .tint(.accent) + Picker(selection: $selectedLanguageForMap) { + ForEach(Settings.availableLanguagesForMap) { languageForMap in + Text(languageForMap.localizedName) + .tag(languageForMap.id) + + if languageForMap.id == "auto" || languageForMap.id == "default" { + Divider() + } + } + } label: { + Text("pref_maplanguage_title") + } + + Toggle(isOn: $shouldTransliterateToLatin) { + VStack(alignment: .leading) { + Text("transliteration_title") + + if selectedLanguageForMap == "default" { + Text("transliteration_title_disabled_summary") + .font(.footnote) + .foregroundStyle(.secondary) + } + } + } + .tint(.accent) + .disabled(selectedLanguageForMap == "default") Picker(selection: $selectedMapAppearance) { ForEach(Settings.Appearance.allCases) { mapAppearance in @@ -267,6 +295,7 @@ struct SettingsView: View { has3dBuildings = Settings.has3dBuildings hasAutomaticDownload = Settings.hasAutomaticDownload hasIncreasedFontsize = Settings.hasIncreasedFontsize + selectedLanguageForMap = Settings.languageForMap shouldTransliterateToLatin = Settings.shouldTransliterateToLatin selectedMapAppearance = Settings.mapAppearance selectedAppearance = Settings.appearance @@ -294,6 +323,11 @@ struct SettingsView: View { .onChange(of: hasIncreasedFontsize) { changedHasIncreasedFontsize in Settings.hasIncreasedFontsize = changedHasIncreasedFontsize } + .onChange(of: selectedLanguageForMap) { changedSelectedLanguageForMap in + if let changedSelectedLanguageForMap { + Settings.languageForMap = changedSelectedLanguageForMap + } + } .onChange(of: shouldTransliterateToLatin) { changedShouldTransliterateToLatin in Settings.shouldTransliterateToLatin = changedShouldTransliterateToLatin } diff --git a/map/framework.cpp b/map/framework.cpp index 6e5dba19f..ff4362e8c 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -2498,6 +2498,16 @@ void Framework::SetMapLanguageCode(std::string const & langCode) m_searchAPI->SetLocale(langCode); } +void Framework::ResetMapLanguageCode() +{ + settings::Delete(settings::kMapLanguageCode); + if (m_drapeEngine) + ApplyMapLanguageCode(languages::GetCurrentMapLanguage()); + + if (m_searchAPI) + m_searchAPI->SetLocale(languages::GetCurrentMapLanguage()); +} + void Framework::ApplyMapLanguageCode(std::string const & langCode) { int8_t langIndex = StringUtf8Multilang::GetLangIndex(langCode); diff --git a/map/framework.hpp b/map/framework.hpp index d57e0cdcb..995698ba6 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -706,6 +706,7 @@ private: public: static std::string GetMapLanguageCode(); void SetMapLanguageCode(std::string const & langCode); + void ResetMapLanguageCode(); void SetLargeFontsSize(bool isLargeSize); bool LoadLargeFontsSize();