1 // Copyright 2021-present 650 Industries. All rights reserved. 2 3 import Foundation 4 import ABI47_0_0ExpoModulesCore 5 6 public class LocalizationModule: Module { definitionnull7 public func definition() -> ModuleDefinition { 8 Name("ExpoLocalization") 9 10 Constants { 11 return Self.getCurrentLocalization() 12 } 13 AsyncFunction("getLocalizationAsync") { 14 return Self.getCurrentLocalization() 15 } 16 Function("getLocales") { 17 return Self.getLocales() 18 } 19 Function("getCalendars") { 20 return Self.getCalendars() 21 } 22 } 23 24 // If the application isn't manually localized for the device language then the 25 // native `Locale.current` will fallback on using English US 26 // [cite](https://stackoverflow.com/questions/48136456/locale-current-reporting-wrong-language-on-device). 27 // This method will attempt to return the locale that the device is using regardless of the app, 28 // providing better parity across platforms. getLocalenull29 static func getLocale() -> Locale { 30 guard let preferredIdentifier = Locale.preferredLanguages.first else { 31 return Locale.current 32 } 33 return Locale(identifier: preferredIdentifier) 34 } 35 /** 36 Maps ios unique identifiers to [BCP 47 calendar types] 37 (https://github.com/unicode-org/cldr/blob/main/common/bcp47/calendar.xml) 38 */ getUnicodeCalendarIdentifiernull39 static func getUnicodeCalendarIdentifier(calendar: Calendar) -> String { 40 switch calendar.identifier { 41 case .buddhist: 42 return "buddhist" 43 case .chinese: 44 return "chinese" 45 case .coptic: 46 return "coptic" 47 case .ethiopicAmeteAlem: 48 return "ethioaa" 49 case .ethiopicAmeteMihret: 50 return "ethiopic" 51 case .gregorian: 52 return "gregory" 53 case .hebrew: 54 return "hebrew" 55 case .indian: 56 return "indian" 57 case .islamic: 58 return "islamic" 59 case .islamicCivil: 60 return "islamic-civil" 61 case .islamicTabular: 62 return "islamic-tbla" 63 case .islamicUmmAlQura: 64 return "islamic-umalqura" 65 case .japanese: 66 return "japanese" 67 case .persian: 68 return "persian" 69 case .republicOfChina: 70 return "roc" 71 case .iso8601: 72 return "iso8601" 73 } 74 } 75 getLocalesnull76 static func getLocales() -> [[String: Any?]] { 77 return (Locale.preferredLanguages.isEmpty ? [Locale.current.identifier] : Locale.preferredLanguages) 78 .map { languageTag -> [String: Any?] in 79 var locale = Locale.init(identifier: languageTag) 80 return [ 81 "languageTag": languageTag, 82 "languageCode": locale.languageCode, 83 "regionCode": locale.regionCode, 84 "textDirection": Locale.characterDirection(forLanguage: languageTag) == .rightToLeft ? "rtl" : "ltr", 85 "decimalSeparator": locale.decimalSeparator, 86 "digitGroupingSeparator": locale.groupingSeparator, 87 "measurementSystem": locale.usesMetricSystem ? "metric" : "us", 88 "currencyCode": locale.currencyCode, 89 "currencySymbol": locale.currencySymbol 90 ] 91 } 92 } 93 94 // https://stackoverflow.com/a/28183182 uses24HourClocknull95 static func uses24HourClock() -> Bool { 96 let dateFormat = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: Locale.current)! 97 98 return dateFormat.firstIndex(of: "a") == nil 99 } 100 getCalendarsnull101 static func getCalendars() -> [[String: Any?]] { 102 var calendar = Locale.current.calendar 103 return [ 104 [ 105 "calendar": getUnicodeCalendarIdentifier(calendar: calendar), 106 "timeZone": "\(calendar.timeZone.identifier)", 107 "uses24hourClock": uses24HourClock(), 108 "firstWeekday": calendar.firstWeekday 109 ] 110 ] 111 } 112 getCurrentLocalizationnull113 static func getCurrentLocalization() -> [String: Any?] { 114 let locale = getLocale() 115 let languageCode = locale.languageCode ?? "en" 116 var languageIds = Locale.preferredLanguages 117 118 if languageIds.isEmpty { 119 languageIds.append("en-US") 120 } 121 return [ 122 "currency": locale.currencyCode ?? "USD", 123 "decimalSeparator": locale.decimalSeparator ?? ".", 124 "digitGroupingSeparator": locale.groupingSeparator ?? ",", 125 "isoCurrencyCodes": Locale.isoCurrencyCodes, 126 "isMetric": locale.usesMetricSystem, 127 "isRTL": Locale.characterDirection(forLanguage: languageCode) == .rightToLeft, 128 "locale": languageIds.first, 129 "locales": languageIds, 130 "region": locale.regionCode ?? "US", 131 "timezone": TimeZone.current.identifier 132 ] 133 } 134 } 135