[ios] Added setting for map language

Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
This commit is contained in:
Yannik Bloscheck
2025-07-24 21:32:04 +02:00
committed by Konstantin Pastbin
parent 1fe8f1f0e6
commit 1cf4ff21ec
12 changed files with 174 additions and 23 deletions

View File

@@ -37,6 +37,10 @@ NS_SWIFT_NAME(SettingsBridge)
+ (BOOL)largeFontSize;
+ (void)setLargeFontSize:(BOOL)largeFontSize;
+ (NSDictionary<NSString *, NSString *> *)availableMapLanguages;
+ (NSString *)mapLanguageCode;
+ (void)setMapLanguageCode:(NSString *)mapLanguageCode;
+ (BOOL)transliteration;
+ (void)setTransliteration:(BOOL)transliteration;

View File

@@ -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<bool>(largeFontSize));
}
+ (NSDictionary<NSString *, NSString *> *)availableMapLanguages;
{
NSMutableDictionary<NSString *, NSString *> *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
{

View File

@@ -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";

View File

@@ -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";

View File

@@ -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 = "<group>"; };
272F1F3C2E0EE0C400FA52EF /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
272F1F452E0EEF8B00FA52EF /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
2752B6C92E31197000887CC4 /* MapLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLanguage.swift; sourceTree = "<group>"; };
2752B6CD2E3121D800887CC4 /* Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = "<group>"; };
2765D1CD2E13F9BC0005CA2B /* BridgeControllers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgeControllers.swift; sourceTree = "<group>"; };
27697F732E25177300FBD913 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
27697F7C2E254A5000FBD913 /* CopyrightView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyrightView.swift; sourceTree = "<group>"; };
@@ -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 */,

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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);

View File

@@ -706,6 +706,7 @@ private:
public:
static std::string GetMapLanguageCode();
void SetMapLanguageCode(std::string const & langCode);
void ResetMapLanguageCode();
void SetLargeFontsSize(bool isLargeSize);
bool LoadLargeFontsSize();