@amazon-devices/expo-localization 2.0.1758683737
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -0
- package/CHANGELOG.md +198 -0
- package/CMakeLists.txt +50 -0
- package/LICENSE +7 -0
- package/README.kepler.md +109 -0
- package/README.md +41 -0
- package/THIRD_PARTY_NOTICES.txt +24 -0
- package/android/build.gradle +109 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/expo/modules/localization/LocalizationModule.kt +207 -0
- package/android/src/main/java/expo/modules/localization/LocalizationPackage.kt +35 -0
- package/android/src/main/java/expo/modules/localization/LocalizationUtils.kt +49 -0
- package/android/src/main/res/values/strings.xml +4 -0
- package/app.plugin.js +1 -0
- package/build/ExpoLocalization.d.ts +22 -0
- package/build/ExpoLocalization.d.ts.map +1 -0
- package/build/ExpoLocalization.js +166 -0
- package/build/ExpoLocalization.js.map +1 -0
- package/build/ExpoLocalization.kepler.d.ts +12 -0
- package/build/ExpoLocalization.kepler.d.ts.map +1 -0
- package/build/ExpoLocalization.kepler.js +26 -0
- package/build/ExpoLocalization.kepler.js.map +1 -0
- package/build/ExpoLocalization.native.d.ts +7 -0
- package/build/ExpoLocalization.native.d.ts.map +1 -0
- package/build/ExpoLocalization.native.js +14 -0
- package/build/ExpoLocalization.native.js.map +1 -0
- package/build/Localization.d.ts +163 -0
- package/build/Localization.d.ts.map +1 -0
- package/build/Localization.js +197 -0
- package/build/Localization.js.map +1 -0
- package/build/Localization.types.d.ts +202 -0
- package/build/Localization.types.d.ts.map +1 -0
- package/build/Localization.types.js +59 -0
- package/build/Localization.types.js.map +1 -0
- package/expo-module.config.json +10 -0
- package/ios/ExpoLocalization.podspec +32 -0
- package/ios/LocalizationModule.swift +212 -0
- package/kepler/ExpoLocalization.cpp +128 -0
- package/kepler/ExpoLocalization.h +22 -0
- package/kepler/KeplerInit.cpp +26 -0
- package/kepler/test/ExpoLocalization.cpp +78 -0
- package/package.json +1 -0
- package/plugin/build/withExpoLocalization.d.ts +7 -0
- package/plugin/build/withExpoLocalization.js +46 -0
- package/plugin/src/withExpoLocalization.ts +62 -0
- package/plugin/tsconfig.json +9 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/react-native.config.js +21 -0
- package/src/ExpoLocalization.kepler.ts +40 -0
- package/src/ExpoLocalization.native.ts +18 -0
- package/src/ExpoLocalization.ts +203 -0
- package/src/Localization.ts +217 -0
- package/src/Localization.types.ts +207 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Localization.types.js","sourceRoot":"","sources":["../src/Localization.types.ts"],"names":[],"mappings":"AA2HA;;GAEG;AACH,MAAM,CAAN,IAAY,OAQX;AARD,WAAY,OAAO;IACjB,yCAAU,CAAA;IACV,yCAAU,CAAA;IACV,2CAAW,CAAA;IACX,+CAAa,CAAA;IACb,6CAAY,CAAA;IACZ,yCAAU,CAAA;IACV,6CAAY,CAAA;AACd,CAAC,EARW,OAAO,KAAP,OAAO,QAQlB;AAED;;;GAGG;AACH,MAAM,CAAN,IAAY,kBAuCX;AAvCD,WAAY,kBAAkB;IAC5B,6BAA6B;IAC7B,2CAAqB,CAAA;IACrB,mCAAmC;IACnC,yCAAmB,CAAA;IACnB,sBAAsB;IACtB,uCAAiB,CAAA;IACjB,kCAAkC;IAClC,qCAAe,CAAA;IACf,+DAA+D;IAC/D,yCAAmB,CAAA;IACnB,6DAA6D;IAC7D,2CAAqB,CAAA;IACrB,yBAAyB;IACzB,yCAAmB,CAAA;IACnB,iCAAiC;IACjC,2CAAqB,CAAA;IACrB,kCAAkC;IAClC,uCAAiB,CAAA;IACjB,sBAAsB;IACtB,uCAAiB,CAAA;IACjB,uBAAuB;IACvB,yCAAmB,CAAA;IACnB,kGAAkG;IAClG,qDAA+B,CAAA;IAC/B,8CAA8C;IAC9C,mDAA6B,CAAA;IAC7B,wGAAwG;IACxG,mDAA6B,CAAA;IAC7B,oCAAoC;IACpC,2DAAqC,CAAA;IACrC,+EAA+E;IAC/E,yCAAmB,CAAA;IACnB,iCAAiC;IACjC,2CAAqB,CAAA;IACrB,uBAAuB;IACvB,yCAAmB,CAAA;IACnB,0CAA0C;IAC1C,iCAAW,CAAA;AACb,CAAC,EAvCW,kBAAkB,KAAlB,kBAAkB,QAuC7B","sourcesContent":["// @needsAudit\nexport type Localization = {\n /**\n * Three-character ISO 4217 currency code. Returns `null` on web.\n *\n * @example `'USD'`, `'EUR'`, `'CNY'`, `null`\n */\n currency: string | null;\n /**\n * Decimal separator used for formatting numbers.\n *\n * @example `','`, `'.'`\n */\n decimalSeparator: string;\n /**\n * Digit grouping separator used when formatting numbers larger than 1000.\n *\n * @example `'.'`, `''`, `','`\n */\n digitGroupingSeparator: string;\n /**\n * A list of all the supported language ISO codes.\n */\n isoCurrencyCodes: string[];\n /**\n * Boolean value that indicates whether the system uses the metric system.\n * On Android and web, this is inferred from the current region.\n */\n isMetric: boolean;\n /**\n * Returns if the system's language is written from Right-to-Left.\n * This can be used to build features like [bidirectional icons](https://material.io/design/usability/bidirectionality.html).\n *\n * Returns `false` in Server Side Rendering (SSR) environments.\n */\n isRTL: boolean;\n /**\n * An [IETF BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag),\n * consisting of a two-character language code and optional script, region and variant codes.\n *\n * @example `'en'`, `'en-US'`, `'zh-Hans'`, `'zh-Hans-CN'`, `'en-emodeng'`\n */\n locale: string;\n /**\n * List of all the native languages provided by the user settings.\n * These are returned in the order that the user defined in the device settings.\n *\n * @example `['en', 'en-US', 'zh-Hans', 'zh-Hans-CN', 'en-emodeng']`\n */\n locales: string[];\n /**\n * The region code for your device that comes from the Region setting under Language & Region on iOS.\n * This value is always available on iOS, but might return `null` on Android or web.\n *\n * @example `'US'`, `'NZ'`, `null`\n */\n region: string | null;\n /**\n * The current time zone in display format.\n * On Web time zone is calculated with Intl.DateTimeFormat().resolvedOptions().timeZone. For a\n * better estimation you could use the moment-timezone package but it will add significant bloat to\n * your website's bundle size.\n *\n * @example `'America/Los_Angeles'`\n */\n timezone: string;\n};\n\nexport type Locale = {\n /**\n * An [IETF BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) with a region code.\n * @example `'en-US'`, `'es-419'`, `'pl-PL'`.\n */\n languageTag: string;\n /**\n * An [IETF BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) without the region code.\n * @example `'en'`, `'es'`, `'pl'`.\n */\n languageCode: string;\n /**\n * The region code for your device that comes from the Region setting under Language & Region on iOS, Region settings on Android and is parsed from locale on Web (can be `null` on Web).\n */\n regionCode: string | null;\n /**\n * Currency code for the locale.\n * Is `null` on Web, use a table lookup based on region instead.\n * @example `'USD'`, `'EUR'`, `'PLN'`.\n */\n currencyCode: string | null;\n /**\n * Currency symbol for the locale.\n * Is `null` on Web, use a table lookup based on region (if available) instead.\n * @example `'$'`, `'€'`, `'zł'`.\n */\n currencySymbol: string | null;\n /**\n * Decimal separator used for formatting numbers with fractional parts.\n * @example `'.'`, `','`.\n */\n decimalSeparator: string | null;\n /**\n * Digit grouping separator used for formatting large numbers.\n * @example `'.'`, `','`.\n */\n digitGroupingSeparator: string | null;\n /**\n * Text direction for the locale. One of: `'ltr'`, `'rtl'`, but can also be `null` on some browsers without support for the [textInfo](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/textInfo) property in [Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) API.\n */\n textDirection: 'ltr' | 'rtl' | null;\n /**\n * The measurement system used in the locale.\n * Is `null` on Web, as user chosen measurement system is not exposed on the web and using locale to determine measurement systems is unreliable.\n * Ask for user preferences if possible.\n */\n measurementSystem: `metric` | `us` | `uk` | null;\n\n /**\n * The temperature unit used in the locale.\n * Returns `null` if the region code is unknown.\n */\n temperatureUnit: 'celsius' | 'fahrenheit' | null;\n};\n\n/**\n * An enum mapping days of the week in Gregorian calendar to their index as returned by the `firstWeekday` property.\n */\nexport enum Weekday {\n SUNDAY = 1,\n MONDAY = 2,\n TUESDAY = 3,\n WEDNESDAY = 4,\n THURSDAY = 5,\n FRIDAY = 6,\n SATURDAY = 7,\n}\n\n/**\n * The calendar identifier, one of [Unicode calendar types](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar).\n * Gregorian calendar is aliased and can be referred to as both `CalendarIdentifier.GREGORIAN` and `CalendarIdentifier.GREGORY`.\n */\nexport enum CalendarIdentifier {\n /** Thai Buddhist calendar */\n BUDDHIST = 'buddhist',\n /** Traditional Chinese calendar */\n CHINESE = 'chinese',\n /** Coptic calendar */\n COPTIC = 'coptic',\n /** Traditional Korean calendar */\n DANGI = 'dangi',\n /** Ethiopic calendar, Amete Alem (epoch approx. 5493 B.C.E) */\n ETHIOAA = 'ethioaa',\n /** Ethiopic calendar, Amete Mihret (epoch approx, 8 C.E.) */\n ETHIOPIC = 'ethiopic',\n /** Gregorian calendar */\n GREGORY = 'gregory',\n /** Gregorian calendar (alias) */\n GREGORIAN = 'gregory',\n /** Traditional Hebrew calendar */\n HEBREW = 'hebrew',\n /** Indian calendar */\n INDIAN = 'indian',\n /** Islamic calendar */\n ISLAMIC = 'islamic',\n /** Islamic calendar, tabular (intercalary years [2,5,7,10,13,16,18,21,24,26,29] - civil epoch) */\n ISLAMIC_CIVIL = 'islamic-civil',\n /** Islamic calendar, Saudi Arabia sighting */\n ISLAMIC_RGSA = 'islamic-rgsa',\n /**Islamic calendar, tabular (intercalary years [2,5,7,10,13,16,18,21,24,26,29] - astronomical epoch) */\n ISLAMIC_TBLA = 'islamic-tbla',\n /** Islamic calendar, Umm al-Qura */\n ISLAMIC_UMALQURA = 'islamic-umalqura',\n /** ISO calendar (Gregorian calendar using the ISO 8601 calendar week rules) */\n ISO8601 = 'iso8601',\n /** Japanese imperial calendar */\n JAPANESE = 'japanese',\n /** Persian calendar */\n PERSIAN = 'persian',\n /** Civil (algorithmic) Arabic calendar */\n ROC = 'roc',\n}\n\nexport type Calendar = {\n /**\n * The calendar identifier, one of [Unicode calendar types](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar).\n *\n * On Android is limited to one of device's [available calendar types](https://developer.android.com/reference/java/util/Calendar#getAvailableCalendarTypes()).\n *\n * On iOS uses [calendar identifiers](https://developer.apple.com/documentation/foundation/calendar/identifier), but maps them to the corresponding Unicode types, will also never contain `'dangi'` or `'islamic-rgsa'` due to it not being implemented on iOS.\n */\n calendar: CalendarIdentifier | null;\n /**\n * True when current device settings use 24 hour time format.\n * Can be null on some browsers that don't support the [hourCycle](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/hourCycle) property in [Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) API.\n */\n uses24hourClock: boolean | null;\n /**\n * The first day of the week. For most calendars Sunday is numbered `1`, with Saturday being number `7`.\n * Can be null on some browsers that don't support the [weekInfo](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/weekInfo) property in [Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) API.\n * @example `1`, `7`.\n */\n firstWeekday: Weekday | null;\n /**\n * Time zone for the calendar. Can be `null` on Web.\n * @example `'America/Los_Angeles'`, `'Europe/Warsaw'`, `'GMT+1'`.\n */\n timeZone: string | null;\n};\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'ExpoLocalization'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.description = package['description']
|
|
10
|
+
s.license = package['license']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.homepage = package['homepage']
|
|
13
|
+
s.platforms = { :ios => '13.0', :tvos => '13.0' }
|
|
14
|
+
s.swift_version = '5.4'
|
|
15
|
+
s.source = { git: 'https://github.com/expo/expo.git' }
|
|
16
|
+
s.static_framework = true
|
|
17
|
+
|
|
18
|
+
s.dependency 'ExpoModulesCore'
|
|
19
|
+
|
|
20
|
+
# Swift/Objective-C compatibility
|
|
21
|
+
s.pod_target_xcconfig = {
|
|
22
|
+
'DEFINES_MODULE' => 'YES',
|
|
23
|
+
'SWIFT_COMPILATION_MODE' => 'wholemodule'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
|
|
27
|
+
s.source_files = "**/*.h"
|
|
28
|
+
s.vendored_frameworks = "#{s.name}.xcframework"
|
|
29
|
+
else
|
|
30
|
+
s.source_files = "**/*.{h,m,swift}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Copyright 2021-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import ExpoModulesCore
|
|
5
|
+
|
|
6
|
+
let LOCALE_SETTINGS_CHANGED = "onLocaleSettingsChanged"
|
|
7
|
+
let CALENDAR_SETTINGS_CHANGED = "onCalendarSettingsChanged"
|
|
8
|
+
|
|
9
|
+
public class LocalizationModule: Module {
|
|
10
|
+
public func definition() -> ModuleDefinition {
|
|
11
|
+
Name("ExpoLocalization")
|
|
12
|
+
|
|
13
|
+
Constants {
|
|
14
|
+
return Self.getCurrentLocalization()
|
|
15
|
+
}
|
|
16
|
+
AsyncFunction("getLocalizationAsync") {
|
|
17
|
+
return Self.getCurrentLocalization()
|
|
18
|
+
}
|
|
19
|
+
Function("getLocales") {
|
|
20
|
+
return Self.getLocales()
|
|
21
|
+
}
|
|
22
|
+
Function("getCalendars") {
|
|
23
|
+
return Self.getCalendars()
|
|
24
|
+
}
|
|
25
|
+
OnCreate {
|
|
26
|
+
if let enableRTL = Bundle.main.object(forInfoDictionaryKey: "ExpoLocalization_supportsRTL") as? Bool {
|
|
27
|
+
self.setSupportsRTL(enableRTL)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Events(LOCALE_SETTINGS_CHANGED, CALENDAR_SETTINGS_CHANGED)
|
|
32
|
+
|
|
33
|
+
OnStartObserving {
|
|
34
|
+
NotificationCenter.default.addObserver(
|
|
35
|
+
self,
|
|
36
|
+
selector: #selector(LocalizationModule.localeChanged),
|
|
37
|
+
name: NSLocale.currentLocaleDidChangeNotification, // swiftlint:disable:this legacy_objc_type
|
|
38
|
+
object: nil
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
OnStopObserving {
|
|
43
|
+
NotificationCenter.default.removeObserver(
|
|
44
|
+
self,
|
|
45
|
+
name: NSLocale.currentLocaleDidChangeNotification, // swiftlint:disable:this legacy_objc_type
|
|
46
|
+
object: nil
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func isRTLPreferredForCurrentLocale() -> Bool {
|
|
52
|
+
// swiftlint:disable:next legacy_objc_type
|
|
53
|
+
return NSLocale.characterDirection(forLanguage: NSLocale.preferredLanguages.first ?? "en-US") == NSLocale.LanguageDirection.rightToLeft
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func setSupportsRTL(_ supportsRTL: Bool) {
|
|
57
|
+
// These keys are used by React Native here: https://github.com/facebook/react-native/blob/main/React/Modules/RCTI18nUtil.m
|
|
58
|
+
// We set them before React loads to ensure it gets rendered correctly the first time the app is opened.
|
|
59
|
+
// On iOS we need to set both forceRTL and allowRTL so apps don't have to include localization strings.
|
|
60
|
+
UserDefaults.standard.set(supportsRTL, forKey: "RCTI18nUtil_allowRTL")
|
|
61
|
+
UserDefaults.standard.set(supportsRTL ? isRTLPreferredForCurrentLocale() : false, forKey: "RCTI18nUtil_forceRTL")
|
|
62
|
+
UserDefaults.standard.synchronize()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// If the application isn't manually localized for the device language then the
|
|
66
|
+
// native `Locale.current` will fallback on using English US
|
|
67
|
+
// [cite](https://stackoverflow.com/questions/48136456/locale-current-reporting-wrong-language-on-device).
|
|
68
|
+
// This method will attempt to return the locale that the device is using regardless of the app,
|
|
69
|
+
// providing better parity across platforms.
|
|
70
|
+
static func getLocale() -> Locale {
|
|
71
|
+
guard let preferredIdentifier = Locale.preferredLanguages.first else {
|
|
72
|
+
return Locale.current
|
|
73
|
+
}
|
|
74
|
+
return Locale(identifier: preferredIdentifier)
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
Maps ios unique identifiers to [BCP 47 calendar types]
|
|
78
|
+
(https://github.com/unicode-org/cldr/blob/main/common/bcp47/calendar.xml)
|
|
79
|
+
*/
|
|
80
|
+
static func getUnicodeCalendarIdentifier(calendar: Calendar) -> String {
|
|
81
|
+
switch calendar.identifier {
|
|
82
|
+
case .buddhist:
|
|
83
|
+
return "buddhist"
|
|
84
|
+
case .chinese:
|
|
85
|
+
return "chinese"
|
|
86
|
+
case .coptic:
|
|
87
|
+
return "coptic"
|
|
88
|
+
case .ethiopicAmeteAlem:
|
|
89
|
+
return "ethioaa"
|
|
90
|
+
case .ethiopicAmeteMihret:
|
|
91
|
+
return "ethiopic"
|
|
92
|
+
case .gregorian:
|
|
93
|
+
return "gregory"
|
|
94
|
+
case .hebrew:
|
|
95
|
+
return "hebrew"
|
|
96
|
+
case .indian:
|
|
97
|
+
return "indian"
|
|
98
|
+
case .islamic:
|
|
99
|
+
return "islamic"
|
|
100
|
+
case .islamicCivil:
|
|
101
|
+
return "islamic-civil"
|
|
102
|
+
case .islamicTabular:
|
|
103
|
+
return "islamic-tbla"
|
|
104
|
+
case .islamicUmmAlQura:
|
|
105
|
+
return "islamic-umalqura"
|
|
106
|
+
case .japanese:
|
|
107
|
+
return "japanese"
|
|
108
|
+
case .persian:
|
|
109
|
+
return "persian"
|
|
110
|
+
case .republicOfChina:
|
|
111
|
+
return "roc"
|
|
112
|
+
case .iso8601:
|
|
113
|
+
return "iso8601"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static func getMeasurementSystemForLocale(_ locale: Locale) -> String {
|
|
118
|
+
if #available(iOS 16, tvOS 16, *) {
|
|
119
|
+
let measurementSystems = [
|
|
120
|
+
Locale.MeasurementSystem.us: "us",
|
|
121
|
+
Locale.MeasurementSystem.uk: "uk",
|
|
122
|
+
Locale.MeasurementSystem.metric: "metric"
|
|
123
|
+
]
|
|
124
|
+
return measurementSystems[locale.measurementSystem] ?? "metric"
|
|
125
|
+
}
|
|
126
|
+
return locale.usesMetricSystem ? "metric" : "us"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static func getLocales() -> [[String: Any?]] {
|
|
130
|
+
let userSettingsLocale = Locale.current
|
|
131
|
+
|
|
132
|
+
return (Locale.preferredLanguages.isEmpty ? [Locale.current.identifier] : Locale.preferredLanguages)
|
|
133
|
+
.map { languageTag -> [String: Any?] in
|
|
134
|
+
let languageLocale = Locale.init(identifier: languageTag)
|
|
135
|
+
|
|
136
|
+
return [
|
|
137
|
+
"languageTag": languageTag,
|
|
138
|
+
"languageCode": languageLocale.languageCode,
|
|
139
|
+
"regionCode": languageLocale.regionCode,
|
|
140
|
+
"textDirection": Locale.characterDirection(forLanguage: languageTag) == .rightToLeft ? "rtl" : "ltr",
|
|
141
|
+
"decimalSeparator": userSettingsLocale.decimalSeparator,
|
|
142
|
+
"digitGroupingSeparator": userSettingsLocale.groupingSeparator,
|
|
143
|
+
"measurementSystem": getMeasurementSystemForLocale(userSettingsLocale),
|
|
144
|
+
"currencyCode": languageLocale.currencyCode,
|
|
145
|
+
"currencySymbol": languageLocale.currencySymbol,
|
|
146
|
+
"temperatureUnit": getTemperatureUnit()
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@objc
|
|
152
|
+
private func localeChanged() {
|
|
153
|
+
// we send both events since on iOS it means both calendar and locale needs an update
|
|
154
|
+
sendEvent(LOCALE_SETTINGS_CHANGED)
|
|
155
|
+
sendEvent(CALENDAR_SETTINGS_CHANGED)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static func getTemperatureUnit() -> String? {
|
|
159
|
+
let formatter = MeasurementFormatter()
|
|
160
|
+
formatter.locale = Locale.current
|
|
161
|
+
|
|
162
|
+
let temperature = Measurement(value: 0, unit: UnitTemperature.celsius)
|
|
163
|
+
let formatted = formatter.string(from: temperature)
|
|
164
|
+
|
|
165
|
+
guard let unitCharacter = formatted.last else {
|
|
166
|
+
return nil
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return unitCharacter == "F" ? "fahrenheit" : "celsius"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// https://stackoverflow.com/a/28183182
|
|
173
|
+
static func uses24HourClock() -> Bool {
|
|
174
|
+
let dateFormat = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: Locale.current)!
|
|
175
|
+
|
|
176
|
+
return dateFormat.firstIndex(of: "a") == nil
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static func getCalendars() -> [[String: Any?]] {
|
|
180
|
+
var calendar = Locale.current.calendar
|
|
181
|
+
return [
|
|
182
|
+
[
|
|
183
|
+
"calendar": getUnicodeCalendarIdentifier(calendar: calendar),
|
|
184
|
+
"timeZone": "\(calendar.timeZone.identifier)",
|
|
185
|
+
"uses24hourClock": uses24HourClock(),
|
|
186
|
+
"firstWeekday": calendar.firstWeekday
|
|
187
|
+
]
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
static func getCurrentLocalization() -> [String: Any?] {
|
|
192
|
+
let locale = getLocale()
|
|
193
|
+
let languageCode = locale.languageCode ?? "en"
|
|
194
|
+
var languageIds = Locale.preferredLanguages
|
|
195
|
+
|
|
196
|
+
if languageIds.isEmpty {
|
|
197
|
+
languageIds.append("en-US")
|
|
198
|
+
}
|
|
199
|
+
return [
|
|
200
|
+
"currency": locale.currencyCode ?? "USD",
|
|
201
|
+
"decimalSeparator": locale.decimalSeparator ?? ".",
|
|
202
|
+
"digitGroupingSeparator": locale.groupingSeparator ?? ",",
|
|
203
|
+
"isoCurrencyCodes": Locale.isoCurrencyCodes,
|
|
204
|
+
"isMetric": locale.usesMetricSystem,
|
|
205
|
+
"isRTL": Locale.characterDirection(forLanguage: languageCode) == .rightToLeft,
|
|
206
|
+
"locale": languageIds.first,
|
|
207
|
+
"locales": languageIds,
|
|
208
|
+
"region": locale.regionCode ?? "US",
|
|
209
|
+
"timezone": TimeZone.current.identifier
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
*
|
|
6
|
+
* Use is subject to license terms.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "ExpoLocalization.h"
|
|
10
|
+
|
|
11
|
+
#include <Volta/i18n/I18nManager.h>
|
|
12
|
+
#include <uchar.h>
|
|
13
|
+
#include <unicode/currunit.h>
|
|
14
|
+
#include <unicode/dcfmtsym.h>
|
|
15
|
+
#include <unicode/dtptngen.h>
|
|
16
|
+
#include <unicode/numfmt.h>
|
|
17
|
+
#include <unicode/ulocdata.h>
|
|
18
|
+
|
|
19
|
+
#include <memory>
|
|
20
|
+
|
|
21
|
+
std::string toUTF8String(icu::UnicodeString from) {
|
|
22
|
+
std::string result;
|
|
23
|
+
from.toUTF8String(result);
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
ExpoLocalization::ExpoLocalization() : volta::react::KeplerTurboModule("ExpoLocalization") {}
|
|
28
|
+
|
|
29
|
+
volta::react::NativeJSArray ExpoLocalization::getLocales() {
|
|
30
|
+
volta::react::NativeJSObject result;
|
|
31
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
32
|
+
|
|
33
|
+
auto appLocale = volta::i18n::I18nManager::getInstance().getAppLocale();
|
|
34
|
+
icu::Locale loc(appLocale.c_str());
|
|
35
|
+
|
|
36
|
+
result["languageCode"] = std::string(loc.getLanguage());
|
|
37
|
+
result["languageTag"] = appLocale;
|
|
38
|
+
|
|
39
|
+
std::unique_ptr<icu::NumberFormat> nf(icu::NumberFormat::createInstance(loc, status));
|
|
40
|
+
if (U_SUCCESS(status)) {
|
|
41
|
+
result["currencyCode"] = toUTF8String(nf->getCurrency());
|
|
42
|
+
} else {
|
|
43
|
+
result["currencyCode"] = nullptr;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
status = U_ZERO_ERROR;
|
|
47
|
+
icu::DecimalFormatSymbols dfs(loc, status);
|
|
48
|
+
if (U_SUCCESS(status)) {
|
|
49
|
+
result["currencySymbol"] = toUTF8String(dfs.getSymbol(icu::DecimalFormatSymbols::kCurrencySymbol));
|
|
50
|
+
result["decimalSeparator"] = toUTF8String(dfs.getSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol));
|
|
51
|
+
result["digitGroupingSeparator"] =
|
|
52
|
+
toUTF8String(dfs.getSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol));
|
|
53
|
+
} else {
|
|
54
|
+
result["currencySymbol"] = nullptr;
|
|
55
|
+
result["decimalSeparator"] = nullptr;
|
|
56
|
+
result["digitGroupingSeparator"] = nullptr;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
status = U_ZERO_ERROR;
|
|
60
|
+
auto measurementSystem = ulocdata_getMeasurementSystem(loc.getLanguage(), &status);
|
|
61
|
+
if (measurementSystem == UMS_SI) {
|
|
62
|
+
result["measurementSystem"] = "metric";
|
|
63
|
+
} else if (measurementSystem == UMS_US) {
|
|
64
|
+
result["measurementSystem"] = "imperial";
|
|
65
|
+
} else if (measurementSystem == UMS_UK) {
|
|
66
|
+
result["measurementSystem"] = "uk";
|
|
67
|
+
} else {
|
|
68
|
+
result["measurementSystem"] = nullptr;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
result["textDirection"] = loc.isRightToLeft() ? "rtl" : "ltr";
|
|
72
|
+
|
|
73
|
+
result["regionCode"] = nullptr;
|
|
74
|
+
|
|
75
|
+
return {result};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
volta::react::NativeJSArray ExpoLocalization::getCalendars() {
|
|
79
|
+
volta::react::NativeJSObject calendar;
|
|
80
|
+
|
|
81
|
+
auto appLocale = volta::i18n::I18nManager::getInstance().getAppLocale();
|
|
82
|
+
icu::Locale loc(appLocale.c_str());
|
|
83
|
+
|
|
84
|
+
std::string timezone = volta::i18n::I18nManager::getInstance().getTimezone();
|
|
85
|
+
icu::UnicodeString timezoneUc(timezone.c_str());
|
|
86
|
+
std::unique_ptr<icu::TimeZone> tz(icu::TimeZone::createTimeZone(timezoneUc));
|
|
87
|
+
|
|
88
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
89
|
+
std::unique_ptr<icu::Calendar> c(icu::Calendar::createInstance(loc, status));
|
|
90
|
+
if (U_SUCCESS(status)) {
|
|
91
|
+
calendar["calendar"] = std::string(c->getType());
|
|
92
|
+
} else {
|
|
93
|
+
calendar["calendar"] = nullptr;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
status = U_ZERO_ERROR;
|
|
97
|
+
int firstWeekday = (int) c->getFirstDayOfWeek(status);
|
|
98
|
+
if (U_SUCCESS(status)) {
|
|
99
|
+
calendar["firstWeekday"] = firstWeekday;
|
|
100
|
+
} else {
|
|
101
|
+
calendar["firstWeekday"] = nullptr;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
calendar["uses24hourClock"] = nullptr;
|
|
105
|
+
status = U_ZERO_ERROR;
|
|
106
|
+
std::unique_ptr<icu::DateTimePatternGenerator> dtp(icu::DateTimePatternGenerator::createInstance(loc, status));
|
|
107
|
+
if (U_SUCCESS(status)) {
|
|
108
|
+
status = U_ZERO_ERROR;
|
|
109
|
+
auto cycle = dtp->getDefaultHourCycle(status);
|
|
110
|
+
if (U_SUCCESS(status)) {
|
|
111
|
+
calendar["uses24hourClock"] = (cycle == UDAT_HOUR_CYCLE_23 || cycle == UDAT_HOUR_CYCLE_24);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (timezone.length() == 0) {
|
|
116
|
+
calendar["timezone"] = nullptr;
|
|
117
|
+
} else {
|
|
118
|
+
calendar["timezone"] = timezone;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {calendar};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void ExpoLocalization::aggregateMethods(
|
|
125
|
+
volta::react::MethodAggregator<volta::react::KeplerTurboModule>& methodAggregator) const noexcept {
|
|
126
|
+
methodAggregator.addMethod("getLocales", 0, &ExpoLocalization::getLocales);
|
|
127
|
+
methodAggregator.addMethod("getCalendars", 0, &ExpoLocalization::getCalendars);
|
|
128
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
*
|
|
6
|
+
* Use is subject to license terms.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
#include <KeplerScript/modules/KeplerTurboModule.h>
|
|
12
|
+
|
|
13
|
+
class KEPLERSCRIPT_EXPORT ExpoLocalization : public volta::react::KeplerTurboModule {
|
|
14
|
+
public:
|
|
15
|
+
ExpoLocalization();
|
|
16
|
+
volta::react::NativeJSArray getLocales();
|
|
17
|
+
volta::react::NativeJSArray getCalendars();
|
|
18
|
+
|
|
19
|
+
protected:
|
|
20
|
+
void aggregateMethods(
|
|
21
|
+
volta::react::MethodAggregator<volta::react::KeplerTurboModule>& methodAggregator) const noexcept override;
|
|
22
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
*
|
|
6
|
+
* Use is subject to license terms.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include <KeplerScript/logger/KPLog.h>
|
|
10
|
+
#include <KeplerScript/utils/autolink/KeplerAutoLinkInfo.h>
|
|
11
|
+
#include <keplerui-072/keplerscript-export.h>
|
|
12
|
+
|
|
13
|
+
#include "ExpoLocalization.h"
|
|
14
|
+
#include "RegisterModule.h"
|
|
15
|
+
|
|
16
|
+
extern "C" {
|
|
17
|
+
KEPLERSCRIPT_EXPORT void keplerAutoLinkInitV1(const kepler::autolink::v1::KeplerAutoLinkInfo* autoLinkInfo) noexcept {
|
|
18
|
+
volta::react::KPLog.debug("keplerAutoLinkInitV1 for ExpoLocalization");
|
|
19
|
+
|
|
20
|
+
REGISTER_MODULE(autoLinkInfo, ExpoLocalization, "ExpoLocalization");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
int main() {
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
*
|
|
6
|
+
* Use is subject to license terms.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "../ExpoLocalization.h"
|
|
10
|
+
|
|
11
|
+
#include <gmock/gmock-actions.h>
|
|
12
|
+
#include <gmock/gmock.h>
|
|
13
|
+
#include <gtest/gtest.h>
|
|
14
|
+
|
|
15
|
+
#include "catch.hpp"
|
|
16
|
+
|
|
17
|
+
namespace {
|
|
18
|
+
template<typename T>
|
|
19
|
+
void assertKey(const volta::react::NativeJSObject& object, const std::string& key, const T& expectedValue) {
|
|
20
|
+
EXPECT_TRUE(object.find(key) != std::end(object)) << "Expected key: " << key;
|
|
21
|
+
EXPECT_EQ(std::get<T>(object.at(key)), expectedValue);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void assertKey(const volta::react::NativeJSObject& object, const std::string& key) {
|
|
25
|
+
EXPECT_TRUE(object.find(key) != std::end(object)) << "Expected key: " << key;
|
|
26
|
+
}
|
|
27
|
+
} // namespace
|
|
28
|
+
|
|
29
|
+
namespace volta::react {
|
|
30
|
+
|
|
31
|
+
TEST_CASE("ExpoLocalization") {
|
|
32
|
+
SECTION("getLocales") {
|
|
33
|
+
ExpoLocalization module;
|
|
34
|
+
|
|
35
|
+
auto locales = module.getLocales();
|
|
36
|
+
EXPECT_EQ(locales.size(), 1);
|
|
37
|
+
|
|
38
|
+
auto locale = std::get<NativeJSObject>(locales[0]);
|
|
39
|
+
|
|
40
|
+
assertKey(locale, "languageCode");
|
|
41
|
+
assertKey(locale, "languageTag");
|
|
42
|
+
|
|
43
|
+
assertKey(locale, "currencyCode");
|
|
44
|
+
assertKey(locale, "currencySymbol");
|
|
45
|
+
assertKey(locale, "digitGroupingSeparator");
|
|
46
|
+
assertKey(locale, "measurementSystem");
|
|
47
|
+
|
|
48
|
+
assertKey<std::string>(locale, "textDirection", "ltr");
|
|
49
|
+
assertKey<std::nullptr_t>(locale, "regionCode", nullptr);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
SECTION("getCalendars") {
|
|
53
|
+
ExpoLocalization module;
|
|
54
|
+
|
|
55
|
+
auto calendars = module.getCalendars();
|
|
56
|
+
EXPECT_EQ(calendars.size(), 1);
|
|
57
|
+
|
|
58
|
+
auto calendar = std::get<NativeJSObject>(calendars[0]);
|
|
59
|
+
|
|
60
|
+
assertKey<std::string>(calendar, "calendar", "gregorian");
|
|
61
|
+
assertKey(calendar, "firstWeekday");
|
|
62
|
+
assertKey(calendar, "uses24hourClock");
|
|
63
|
+
assertKey<std::nullptr_t>(calendar, "timezone", nullptr);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
SECTION("aggregateMethods") {
|
|
67
|
+
ExpoLocalization module;
|
|
68
|
+
|
|
69
|
+
std::weak_ptr<facebook::react::Instance> instance;
|
|
70
|
+
|
|
71
|
+
module.triggerMethodAggregation(*module.jsQueue_, instance);
|
|
72
|
+
|
|
73
|
+
auto end = module.methodContainerMap_.end();
|
|
74
|
+
REQUIRE(module.methodContainerMap_.find("getLocales") != end);
|
|
75
|
+
REQUIRE(module.methodContainerMap_.find("getCalendars") != end);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} // namespace volta::react
|
package/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name": "@amazon-devices/expo-localization", "version": "2.0.1758683737+14.6.0", "description": "Provides an interface for native user localization information.", "main": "build/Localization.js", "types": "build/Localization.d.ts", "sideEffects": false, "scripts": {"build": "expo-module build", "clean": "expo-module clean", "lint": "expo-module lint", "test": "expo-module test", "expo-module": "expo-module"}, "kepler": {"temporary-alias-for-package": {"name": "expo-localization", "version": "14.6.0"}}, "keywords": ["react-native", "expo", "localization", "locales", "l10n"], "repository": {"type": "git", "url": "https://github.com/expo/expo.git", "directory": "packages/expo-localization"}, "bugs": {"url": "https://github.com/expo/expo/issues"}, "contributors": ["Evan Bacon <bacon@expo.io> (https://github.com/evanbacon)"], "author": "650 Industries, Inc.", "license": "See license in LICENSE", "homepage": "https://docs.expo.dev/versions/latest/sdk/localization/", "jest": {"preset": "expo-module-scripts"}, "dependencies": {"rtl-detect": "^1.0.2"}, "devDependencies": {"expo-module-scripts": "^3.0.0", "i18n-js": "^3.1.0"}, "peerDependencies": {"expo": "*"}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExpoConfig } from '@expo/config-types';
|
|
2
|
+
type ConfigPluginProps = {
|
|
3
|
+
supportsRTL?: boolean;
|
|
4
|
+
allowDynamicLocaleChangesAndroid?: boolean;
|
|
5
|
+
};
|
|
6
|
+
declare function withExpoLocalization(config: ExpoConfig, data?: ConfigPluginProps): ExpoConfig;
|
|
7
|
+
export default withExpoLocalization;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Manifest_1 = require("@expo/config-plugins/build/android/Manifest");
|
|
4
|
+
const config_plugins_1 = require("expo/config-plugins");
|
|
5
|
+
function withExpoLocalizationIos(config) {
|
|
6
|
+
if (config.extra?.supportsRTL == null)
|
|
7
|
+
return config;
|
|
8
|
+
if (!config.ios)
|
|
9
|
+
config.ios = {};
|
|
10
|
+
if (!config.ios.infoPlist)
|
|
11
|
+
config.ios.infoPlist = {};
|
|
12
|
+
config.ios.infoPlist.ExpoLocalization_supportsRTL = config.extra?.supportsRTL || false;
|
|
13
|
+
return config;
|
|
14
|
+
}
|
|
15
|
+
function withExpoLocalizationAndroid(config, data) {
|
|
16
|
+
if (data.allowDynamicLocaleChangesAndroid) {
|
|
17
|
+
config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
18
|
+
const mainActivity = (0, Manifest_1.getMainActivityOrThrow)(config.modResults);
|
|
19
|
+
if (!mainActivity.$['android:configChanges']?.includes('locale')) {
|
|
20
|
+
mainActivity.$['android:configChanges'] += '|locale';
|
|
21
|
+
}
|
|
22
|
+
if (!mainActivity.$['android:configChanges']?.includes('layoutDirection')) {
|
|
23
|
+
mainActivity.$['android:configChanges'] += '|layoutDirection';
|
|
24
|
+
}
|
|
25
|
+
return config;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return (0, config_plugins_1.withStringsXml)(config, (config) => {
|
|
29
|
+
config.modResults = config_plugins_1.AndroidConfig.Strings.setStringItem([
|
|
30
|
+
{
|
|
31
|
+
$: { name: 'ExpoLocalization_supportsRTL', translatable: 'false' },
|
|
32
|
+
_: String(data.supportsRTL ?? config.extra?.supportsRTL),
|
|
33
|
+
},
|
|
34
|
+
], config.modResults);
|
|
35
|
+
return config;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function withExpoLocalization(config, data = {
|
|
39
|
+
allowDynamicLocaleChangesAndroid: true,
|
|
40
|
+
}) {
|
|
41
|
+
return (0, config_plugins_1.withPlugins)(config, [
|
|
42
|
+
[withExpoLocalizationIos, data],
|
|
43
|
+
[withExpoLocalizationAndroid, data],
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
exports.default = withExpoLocalization;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getMainActivityOrThrow } from '@expo/config-plugins/build/android/Manifest';
|
|
2
|
+
import { ExpoConfig } from '@expo/config-types';
|
|
3
|
+
import {
|
|
4
|
+
AndroidConfig,
|
|
5
|
+
withAndroidManifest,
|
|
6
|
+
withPlugins,
|
|
7
|
+
withStringsXml,
|
|
8
|
+
} from 'expo/config-plugins';
|
|
9
|
+
|
|
10
|
+
type ConfigPluginProps = {
|
|
11
|
+
supportsRTL?: boolean;
|
|
12
|
+
allowDynamicLocaleChangesAndroid?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function withExpoLocalizationIos(config: ExpoConfig) {
|
|
16
|
+
if (config.extra?.supportsRTL == null) return config;
|
|
17
|
+
if (!config.ios) config.ios = {};
|
|
18
|
+
if (!config.ios.infoPlist) config.ios.infoPlist = {};
|
|
19
|
+
config.ios.infoPlist.ExpoLocalization_supportsRTL = config.extra?.supportsRTL || false;
|
|
20
|
+
return config;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function withExpoLocalizationAndroid(config: ExpoConfig, data: ConfigPluginProps) {
|
|
24
|
+
if (data.allowDynamicLocaleChangesAndroid) {
|
|
25
|
+
config = withAndroidManifest(config, (config) => {
|
|
26
|
+
const mainActivity = getMainActivityOrThrow(config.modResults);
|
|
27
|
+
if (!mainActivity.$['android:configChanges']?.includes('locale')) {
|
|
28
|
+
mainActivity.$['android:configChanges'] += '|locale';
|
|
29
|
+
}
|
|
30
|
+
if (!mainActivity.$['android:configChanges']?.includes('layoutDirection')) {
|
|
31
|
+
mainActivity.$['android:configChanges'] += '|layoutDirection';
|
|
32
|
+
}
|
|
33
|
+
return config;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return withStringsXml(config, (config) => {
|
|
37
|
+
config.modResults = AndroidConfig.Strings.setStringItem(
|
|
38
|
+
[
|
|
39
|
+
{
|
|
40
|
+
$: { name: 'ExpoLocalization_supportsRTL', translatable: 'false' },
|
|
41
|
+
_: String(data.supportsRTL ?? config.extra?.supportsRTL),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
config.modResults
|
|
45
|
+
);
|
|
46
|
+
return config;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function withExpoLocalization(
|
|
51
|
+
config: ExpoConfig,
|
|
52
|
+
data: ConfigPluginProps = {
|
|
53
|
+
allowDynamicLocaleChangesAndroid: true,
|
|
54
|
+
}
|
|
55
|
+
) {
|
|
56
|
+
return withPlugins(config, [
|
|
57
|
+
[withExpoLocalizationIos, data],
|
|
58
|
+
[withExpoLocalizationAndroid, data],
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default withExpoLocalization;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/withExpoLocalization.ts"],"version":"5.7.2"}
|