├── android ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── io │ │ └── fixd │ │ └── rctlocale │ │ ├── RCTLocalePackage.java │ │ └── RCTLocaleModule.java └── build.gradle ├── ios ├── RCTLocale.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj └── RCTLocale │ ├── RCTLocale.h │ └── RCTLocale.m ├── package.json ├── index.android.js ├── index.ios.js ├── react-native-locale.podspec ├── LocaleBaseClass.js ├── .gitignore └── README.md /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /ios/RCTLocale.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-locale", 3 | "version": "0.0.19", 4 | "description": "Simple locale information and methods for react native", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/fixdio/react-native-locale.git" 8 | }, 9 | "author": "Fixd", 10 | "license": "MIT" 11 | } 12 | -------------------------------------------------------------------------------- /ios/RCTLocale/RCTLocale.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTLocale.h 3 | // RCTLocale 4 | // 5 | 6 | #import 7 | #import "React/RCTBridgeModule.h" 8 | #import "React/RCTLog.h" 9 | #import "React/RCTConvert.h" 10 | 11 | @interface RCTConvert (NSDateFormatterStyle) 12 | + (NSDateFormatterStyle)NSDateFormatterStyle:(id)json; 13 | @end 14 | 15 | @interface RCTLocale : NSObject 16 | @end 17 | -------------------------------------------------------------------------------- /index.android.js: -------------------------------------------------------------------------------- 1 | const LocaleBaseClass = require('./LocaleBaseClass'); 2 | 3 | class Locale extends LocaleBaseClass { 4 | 5 | /** 6 | * Android can't pass Long's as ReactMethod arguments so send a string that is parsed 7 | */ 8 | static dateFormat(date : Date, dateStyle : string, timeStyle : string) { 9 | return LocaleBaseClass.dateFormat(''+date.getTime(), dateStyle, timeStyle); 10 | } 11 | 12 | } 13 | 14 | module.exports = Locale; 15 | -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | import {NativeModules} from 'react-native'; 2 | const NativeLocale = NativeModules.Locale; 3 | const LocaleBaseClass = require('./LocaleBaseClass'); 4 | 5 | class Locale extends LocaleBaseClass { 6 | static currencyStyle(number : number) { 7 | return NativeLocale.currencyStyle(number); 8 | } 9 | static percentStyle(number : number) { 10 | return NativeLocale.percentStyle(number); 11 | } 12 | static scientificStyle(number : number) { 13 | return NativeLocale.scientificStyle(number); 14 | } 15 | static spelloutStyle(number : number) { 16 | return NativeLocale.scientificStyle(number); 17 | } 18 | } 19 | 20 | module.exports = Locale; 21 | -------------------------------------------------------------------------------- /react-native-locale.podspec: -------------------------------------------------------------------------------- 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 = package['name'] 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['repository']['url'] 13 | s.source = { :git => 'https://github.com/fixdio/react-native-locale.git', :tag => s.version } 14 | 15 | s.requires_arc = true 16 | s.platform = :ios, '7.0' 17 | 18 | s.preserve_paths = 'README.md', 'package.json', 'index.ios.js' 19 | s.source_files = 'ios/RCTLocale/*.{h,m}' 20 | 21 | s.dependency 'React' 22 | end 23 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def safeExtGet(prop, fallback) { 4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 5 | } 6 | 7 | android { 8 | compileSdkVersion safeExtGet('compileSdkVersion', 23) 9 | buildToolsVersion safeExtGet('buildToolsVersion', "23.0.1") 10 | 11 | defaultConfig { 12 | minSdkVersion safeExtGet('minSdkVersion', 16) 13 | targetSdkVersion safeExtGet('targetSdkVersion', 22) 14 | versionCode 1 15 | versionName "1.0" 16 | } 17 | lintOptions { 18 | abortOnError false 19 | } 20 | } 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | 26 | dependencies { 27 | implementation 'com.facebook.react:react-native:0.17.+' 28 | } -------------------------------------------------------------------------------- /LocaleBaseClass.js: -------------------------------------------------------------------------------- 1 | import { Platform, NativeModules } from 'react-native'; 2 | const NativeLocale = NativeModules.Locale; 3 | const warning = require('fbjs/lib/warning'); 4 | 5 | class LocaleBaseClass { 6 | 7 | static constants() { 8 | return NativeLocale; 9 | } 10 | 11 | static numberFromDecimalString(number : string) { 12 | return NativeLocale.numberFromDecimalString(number); 13 | } 14 | 15 | static decimalStyle(number : number) { 16 | return NativeLocale.decimalStyle(number); 17 | } 18 | 19 | static validateDateFormatStyle(style : string) { 20 | let valid = ['full', 'long', 'medium', 'short', 'none']; 21 | if(Platform.OS == 'ios') 22 | valid.push('default'); 23 | return valid.indexOf(style) >= 0; 24 | } 25 | 26 | static dateFormat(date, dateStyle : string, timeStyle : string) { 27 | warning(LocaleBaseClass.validateDateFormatStyle(dateStyle), 'Locale: DateStyle must be one of [full/long/medium/short/none]'); 28 | warning(LocaleBaseClass.validateDateFormatStyle(timeStyle), 'Locale: TimeStyle must be one of [full/long/medium/short/none]'); 29 | return NativeLocale.dateFormat(date, dateStyle, timeStyle); 30 | } 31 | 32 | } 33 | 34 | module.exports = LocaleBaseClass; 35 | -------------------------------------------------------------------------------- /android/src/main/java/io/fixd/rctlocale/RCTLocalePackage.java: -------------------------------------------------------------------------------- 1 | package io.fixd.rctlocale; 2 | 3 | import android.app.Activity; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.JavaScriptModule; 7 | import com.facebook.react.bridge.NativeModule; 8 | import com.facebook.react.bridge.ReactApplicationContext; 9 | import com.facebook.react.uimanager.ViewManager; 10 | 11 | import java.util.Arrays; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.ArrayList; 15 | 16 | public class RCTLocalePackage implements ReactPackage { 17 | 18 | private Activity mActivity = null; 19 | 20 | @Override 21 | public List createNativeModules(ReactApplicationContext reactContext) { 22 | List modules = new ArrayList<>(); 23 | modules.add(new RCTLocaleModule(reactContext)); 24 | return modules; 25 | } 26 | 27 | // Deprecated in React Native 0.47 28 | public List> createJSModules() { 29 | return Collections.emptyList(); 30 | } 31 | 32 | @Override 33 | public List createViewManagers(ReactApplicationContext reactContext) { 34 | return Collections.emptyList(); 35 | } 36 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/xcode,node 3 | 4 | ### Xcode ### 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | 24 | ## Other 25 | *.xccheckout 26 | *.moved-aside 27 | *.xcuserstate 28 | 29 | 30 | ### Node ### 31 | # Logs 32 | logs 33 | *.log 34 | npm-debug.log* 35 | 36 | # Runtime data 37 | pids 38 | *.pid 39 | *.seed 40 | 41 | # Directory for instrumented libs generated by jscoverage/JSCover 42 | lib-cov 43 | 44 | # Coverage directory used by tools like istanbul 45 | coverage 46 | 47 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 48 | .grunt 49 | 50 | # node-waf configuration 51 | .lock-wscript 52 | 53 | # Compiled binary addons (http://nodejs.org/api/addons.html) 54 | build/Release 55 | 56 | # Dependency directory 57 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 58 | node_modules 59 | 60 | # Optional npm cache directory 61 | .npm 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | .DS_Store 66 | /.idea 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-locale 2 | 3 | Formats data based on locale settings. 4 | 5 | https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/InternationalizingLocaleData/InternationalizingLocaleData.html 6 | 7 | ### RN < 0.47.0 8 | 9 | Please use `0.0.17` for any version of RN before `0.47.0` 10 | 11 | `npm install react-native-locale@0.0.17 --save` 12 | 13 | ### RN < 0.40.0 14 | 15 | Please use `0.0.13` for any version of RN before `0.40.0` 16 | 17 | `npm install react-native-locale@0.0.13 --save` 18 | 19 | ## Installation 20 | 21 | - `npm install react-native-locale --save` 22 | - `react-native link react-native-locale` 23 | 24 | ### Add libraries manually 25 | 26 | #### iOS 27 | 28 | - Link manually 29 | - Add RCTLocale.xcodeproj to Libraries and add libRCTLocale.a to Link Binary With Libraries under Build Phases. 30 | - Cocoapods 31 | - Add following to your `Podfile` 32 | 33 | ```ruby 34 | # Podfile 35 | pod 'react-native-locale', :path => './node_modules/react-native-locale' 36 | ``` 37 | 38 | #### Android 39 | 40 | ``` 41 | // file: android/settings.gradle 42 | ... 43 | 44 | include ':react-native-locale' 45 | project(':react-native-locale').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-locale/android') 46 | ``` 47 | 48 | ``` 49 | // file: android/app/build.gradle 50 | ... 51 | 52 | dependencies { 53 | ... 54 | compile project(':react-native-locale') // <- Add this 55 | } 56 | ``` 57 | 58 | ``` 59 | // file: android/app/source/main/java/com/{projectName}.MainApplication.java 60 | ... 61 | import io.fixd.rctlocale.RCTLocalePackage; 62 | ... 63 | public class MainApplication extends Application implements ReactApplication { 64 | // ... 65 | @Override 66 | protected List getPackages() { 67 | return Arrays.asList( 68 | new MainReactPackage(), 69 | new RCTLocalePackage() // add package 70 | ); 71 | } 72 | ... 73 | ``` 74 | 75 | ## Usage 76 | 77 | For locale information: 78 | 79 | `var Locale = require('react-native-locale');` 80 | 81 | ### Constants 82 | 83 | `Locale.constants()` Returns an object of (Danish locale): 84 | 85 | ```js 86 | { 87 | "localeIdentifier":"en_DK", 88 | "decimalSeparator":",", 89 | "quotationBeginDelimiterKey":"“", 90 | "quotationEndDelimiterKey":"”", 91 | "currencySymbol":"DKK", 92 | "currencyCode":"DKK", 93 | "groupingSeparator":".", 94 | // ios only: 95 | "usesMetricSystem":true, 96 | "localeLanguageCode":"en", 97 | "countryCode":"DK", 98 | "calendar":"gregorian", 99 | "collatorIdentifier":"en-DK", 100 | "alternateQuotationBeginDelimiterKey":"‘", 101 | "alternateQuotationEndDelimiterKey":"’", 102 | "measurementSystem":"Metric", 103 | "preferredLanguages":["en-DK"] 104 | } 105 | ``` 106 | 107 | USA Locale: 108 | 109 | ```js 110 | { 111 | "localeIdentifier":"en_US", 112 | "decimalSeparator":".", 113 | "quotationBeginDelimiterKey":"“", 114 | "quotationEndDelimiterKey":"”", 115 | "currencySymbol":"$", 116 | "currencyCode":"USD", 117 | // ios only: 118 | "usesMetricSystem":false, 119 | "localeLanguageCode":"en", 120 | "countryCode":"US", 121 | "calendar":"gregorian", 122 | "groupingSeparator":",", 123 | "collatorIdentifier":"en-US", 124 | "alternateQuotationBeginDelimiterKey":"‘", 125 | "alternateQuotationEndDelimiterKey":"’", 126 | "measurementSystem":"U.S.", 127 | "preferredLanguages":["en-US"] 128 | 129 | ``` 130 | 131 | ### Numerical formatting 132 | 133 | 134 | ```js 135 | Locale.decimalStyle(12501.50).then((response) => { 136 | console.log(response); 137 | }); 138 | Locale.numberFromDecimalString('125.01,10').then((response) => { 139 | console.log('then', response); 140 | }); 141 | 142 | // ios only 143 | Locale.currencyStyle(12501.50).then((response) => { 144 | console.log(response); 145 | }); 146 | Locale.percentStyle(125.50).then((response) => { 147 | console.log(response); 148 | }); 149 | Locale.scientificStyle(12501.50).then((response) => { 150 | console.log(response); 151 | }); 152 | Locale.spelloutStyle(12501.50).then((response) => { 153 | console.log(response); 154 | }); 155 | ``` 156 | 157 | In Danish locale this returns: 158 | 159 | ``` 160 | 12.501,5 161 | 12.501,50 DKK 162 | 12.550 % 163 | 1,25015E4 164 | twelve thousand five hundred one point five 165 | 125010.1 166 | ``` 167 | 168 | ### Date formatting 169 | 170 | Note: iOS will allow `timestamp` to be either a timestamp, or an ISO-8601 string, the Android java however, won't. The module will try to handle this for you. 171 | 172 | ```js 173 | l.dateFormat(DateObject, DateFormatStyle, TimeFormatStyle) 174 | l.dateFormat(Date, enum('full', 'long', 'medium', 'short', 'none'), enum('full', 'long', 'medium', 'short', 'none')) 175 | `` 176 | 177 | Danish: 178 | ```js 179 | let date = new Date(2016, 4, 26, 16, 38, 22); 180 | l.dateFormat(date.getTime(), 'full', 'full').then((date) => { // Thursday 26 May 2016 at 16 h 38 min 22 s Central European Summer Time 181 | console.log(date); 182 | }); 183 | 184 | l.dateFormat(date.getTime(), 'long', 'long').then((date) => { // 26 May 2016 at 16:38:22 GMT+2 185 | console.log(date); 186 | }); 187 | 188 | l.dateFormat(date.getTime(), 'medium', 'medium').then((date) => { //26 May 2016 16:38:22 189 | console.log(date); 190 | }); 191 | 192 | l.dateFormat(date.getTime(), 'short', 'short').then((date) => { // 26/05/16 16:38 193 | console.log(date); 194 | }); 195 | 196 | l.dateFormat(date.getTime(), 'short', 'none').then((date) => { // 26/05/16 197 | console.log(date); 198 | }); 199 | ``` 200 | 201 | USA: 202 | ``` 203 | Thursday, May 26, 2016 at 4:38:22 PM Central European Summer Time 204 | May 26, 2016 at 4:38:22 PM GMT+2 205 | May 26, 2016, 4:38:22 PM 206 | 5/26/16, 4:38 PM 207 | 5/26/16 208 | ``` 209 | 210 | Android in 24hr Denmark with timezone set to EDT: 211 | 212 | ``` 213 | Thursday, May 26, 2016 3:38:22 PM Eastern Daylight Time 214 | May 26, 2016 3:38:22 PM EDT 215 | May 26, 2016 15:38:22 216 | 5/26/16 15:38 217 | 5/26/16 218 | ``` 219 | -------------------------------------------------------------------------------- /ios/RCTLocale/RCTLocale.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTLocale.m 3 | // RCTLocale 4 | // 5 | 6 | #import "RCTLocale.h" 7 | #import "React/RCTUtils.h" 8 | 9 | @implementation RCTConvert (NSDateFormatterStyle) 10 | RCT_ENUM_CONVERTER(NSDateFormatterStyle, (@{ 11 | @"none": @(NSDateFormatterNoStyle), 12 | @"full": @(NSDateFormatterFullStyle), 13 | @"long": @(NSDateFormatterLongStyle), 14 | @"medium": @(NSDateFormatterMediumStyle), 15 | @"short": @(NSDateFormatterShortStyle), 16 | }), NSDateFormatterFullStyle, integerValue); 17 | @end 18 | 19 | @interface RCTLocale () 20 | @end 21 | 22 | @implementation RCTLocale 23 | RCT_EXPORT_MODULE(); 24 | 25 | + (BOOL)requiresMainQueueSetup 26 | { 27 | return YES; 28 | } 29 | 30 | RCT_EXPORT_METHOD(decimalStyle:(nonnull NSNumber *)myNumber 31 | resolver:(RCTPromiseResolveBlock)resolve 32 | rejecter:(RCTPromiseRejectBlock)reject){ 33 | resolve([NSNumberFormatter localizedStringFromNumber:myNumber numberStyle:NSNumberFormatterDecimalStyle]); 34 | } 35 | 36 | RCT_EXPORT_METHOD(currencyStyle:(nonnull NSNumber *)myNumber 37 | resolver:(RCTPromiseResolveBlock)resolve 38 | rejecter:(RCTPromiseRejectBlock)reject){ 39 | resolve([NSNumberFormatter localizedStringFromNumber:myNumber numberStyle:NSNumberFormatterCurrencyStyle]); 40 | } 41 | 42 | RCT_EXPORT_METHOD(percentStyle:(nonnull NSNumber *)myNumber 43 | resolver:(RCTPromiseResolveBlock)resolve 44 | rejecter:(RCTPromiseRejectBlock)reject){ 45 | resolve([NSNumberFormatter localizedStringFromNumber:myNumber numberStyle:NSNumberFormatterPercentStyle]); 46 | } 47 | 48 | RCT_EXPORT_METHOD(scientificStyle:(nonnull NSNumber *)myNumber 49 | resolver:(RCTPromiseResolveBlock)resolve 50 | rejecter:(RCTPromiseRejectBlock)reject){ 51 | resolve([NSNumberFormatter localizedStringFromNumber:myNumber numberStyle:NSNumberFormatterScientificStyle]); 52 | } 53 | 54 | RCT_EXPORT_METHOD(spelloutStyle:(nonnull NSNumber *)myNumber 55 | resolver:(RCTPromiseResolveBlock)resolve 56 | rejecter:(RCTPromiseRejectBlock)reject){ 57 | resolve([NSNumberFormatter localizedStringFromNumber:myNumber numberStyle:NSNumberFormatterSpellOutStyle]); 58 | } 59 | 60 | RCT_EXPORT_METHOD(numberFromDecimalString:(nonnull NSString *)inputString 61 | resolver:(RCTPromiseResolveBlock)resolve 62 | rejecter:(RCTPromiseRejectBlock)reject){ 63 | NSNumberFormatter *numberFormatter = [NSNumberFormatter new]; 64 | numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; 65 | numberFormatter.lenient = YES; 66 | NSNumber *number = [numberFormatter numberFromString:inputString]; 67 | if(number) { 68 | resolve(number); 69 | } else { 70 | reject(nil, nil, nil); 71 | } 72 | } 73 | 74 | RCT_EXPORT_METHOD(dateFormat:(nonnull NSDate *)date 75 | dateStyle:(NSDateFormatterStyle)dateStyle 76 | timeStyle:(NSDateFormatterStyle)timeStyle 77 | resolver:(RCTPromiseResolveBlock)resolve 78 | rejecter:(RCTPromiseRejectBlock)reject){ 79 | 80 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 81 | dateFormatter.dateStyle = dateStyle; 82 | dateFormatter.timeStyle = timeStyle; 83 | 84 | resolve([dateFormatter stringFromDate:date]); 85 | } 86 | 87 | static id ObjectOrNull(id object) 88 | { 89 | return object ?: [NSNull null]; 90 | } 91 | 92 | - (NSDictionary *)constantsToExport 93 | { 94 | 95 | NSLocale *locale = [NSLocale currentLocale]; 96 | NSCalendar *cal = [locale objectForKey:NSLocaleCalendar]; 97 | 98 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 99 | dateFormatter.dateStyle = NSDateFormatterFullStyle; 100 | NSMutableDictionary *formats = [[NSMutableDictionary alloc]initWithCapacity:4]; 101 | [formats setObject:dateFormatter.dateFormat forKey:@"full"]; 102 | dateFormatter.dateStyle = NSDateFormatterLongStyle; 103 | [formats setObject:dateFormatter.dateFormat forKey:@"long"]; 104 | dateFormatter.dateStyle = NSDateFormatterMediumStyle; 105 | [formats setObject:dateFormatter.dateFormat forKey:@"medium"]; 106 | dateFormatter.dateStyle = NSDateFormatterShortStyle; 107 | [formats setObject:dateFormatter.dateFormat forKey:@"short"]; 108 | 109 | return @{ 110 | @"localeIdentifier": ObjectOrNull([locale objectForKey:NSLocaleIdentifier]), 111 | @"localeLanguageCode": ObjectOrNull([locale objectForKey:NSLocaleLanguageCode]), 112 | @"countryCode": ObjectOrNull([locale objectForKey:NSLocaleCountryCode]), 113 | @"calendar": ObjectOrNull(cal.calendarIdentifier), 114 | @"usesMetricSystem": ObjectOrNull([locale objectForKey:NSLocaleUsesMetricSystem]), 115 | @"measurementSystem": ObjectOrNull([locale objectForKey:NSLocaleMeasurementSystem]), 116 | @"decimalSeparator": ObjectOrNull([locale objectForKey:NSLocaleDecimalSeparator]), 117 | @"groupingSeparator": ObjectOrNull([locale objectForKey:NSLocaleGroupingSeparator]), 118 | @"currencySymbol": ObjectOrNull([locale objectForKey:NSLocaleCurrencySymbol]), 119 | @"currencyCode": ObjectOrNull([locale objectForKey:NSLocaleCurrencyCode]), 120 | @"collatorIdentifier": ObjectOrNull([locale objectForKey:NSLocaleCollatorIdentifier]), 121 | @"quotationBeginDelimiterKey": ObjectOrNull([locale objectForKey:NSLocaleQuotationBeginDelimiterKey]), 122 | @"quotationEndDelimiterKey": ObjectOrNull([locale objectForKey:NSLocaleQuotationEndDelimiterKey]), 123 | @"alternateQuotationBeginDelimiterKey": ObjectOrNull([locale objectForKey:NSLocaleAlternateQuotationBeginDelimiterKey]), 124 | @"alternateQuotationEndDelimiterKey": ObjectOrNull([locale objectForKey:NSLocaleAlternateQuotationEndDelimiterKey]), 125 | @"preferredLanguages": ObjectOrNull([NSLocale preferredLanguages]), 126 | @"localeDateFormats": ObjectOrNull(formats) 127 | }; 128 | } 129 | @end 130 | -------------------------------------------------------------------------------- /android/src/main/java/io/fixd/rctlocale/RCTLocaleModule.java: -------------------------------------------------------------------------------- 1 | package io.fixd.rctlocale; 2 | 3 | import com.facebook.react.bridge.NativeModule; 4 | import com.facebook.react.bridge.ReactApplicationContext; 5 | import com.facebook.react.bridge.ReactContext; 6 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 7 | import com.facebook.react.bridge.ReactMethod; 8 | import com.facebook.react.bridge.Promise; 9 | 10 | import java.lang.String; 11 | import java.lang.IllegalArgumentException; 12 | import java.text.DateFormat; 13 | import java.util.Map; 14 | import java.util.HashMap; 15 | import java.util.Locale; 16 | import java.util.Date; 17 | import java.util.Currency; 18 | import java.text.DecimalFormatSymbols; 19 | import java.text.NumberFormat; 20 | import java.text.DecimalFormat; 21 | import java.lang.Long; 22 | import java.text.SimpleDateFormat; 23 | 24 | public class RCTLocaleModule extends ReactContextBaseJavaModule { 25 | 26 | private ReactApplicationContext mContext; 27 | 28 | public RCTLocaleModule(ReactApplicationContext reactContext) { 29 | super(reactContext); 30 | mContext = reactContext; 31 | } 32 | 33 | @Override 34 | public String getName() { 35 | return "RCTLocale"; 36 | } 37 | 38 | @Override 39 | public Map getConstants() { 40 | 41 | Locale current = getLocale(); 42 | DecimalFormatSymbols formatterSymbols = getDecimalFormat().getDecimalFormatSymbols(); 43 | Currency currency = null; 44 | try { 45 | currency = Currency.getInstance(current); 46 | } catch (IllegalArgumentException e) { 47 | // Exception is ignorable because it means the locale doesn't have a currency 48 | // associated with it. 49 | } 50 | 51 | final Map constants = new HashMap<>(); 52 | constants.put("localeIdentifier", current.toString()); 53 | constants.put("countryCode", current.getCountry()); 54 | constants.put("decimalSeparator", String.valueOf(formatterSymbols.getDecimalSeparator())); 55 | constants.put("groupingSeparator", String.valueOf(formatterSymbols.getGroupingSeparator())); 56 | constants.put("usesMetricSystem", !current.getISO3Country().equalsIgnoreCase("usa")); 57 | constants.put("currencySymbol", currency != null ? currency.getSymbol() : null); 58 | constants.put("currencyCode", currency!= null ? currency.getCurrencyCode() : null); 59 | 60 | final Map formats = new HashMap<>(); 61 | DateFormat dateFormatter; 62 | dateFormatter = DateFormat.getDateInstance(DateFormat.FULL, getLocale()); 63 | formats.put("full", ((SimpleDateFormat) dateFormatter).toLocalizedPattern()); 64 | dateFormatter = DateFormat.getDateInstance(DateFormat.LONG, getLocale()); 65 | formats.put("long", ((SimpleDateFormat) dateFormatter).toLocalizedPattern()); 66 | dateFormatter = DateFormat.getDateInstance(DateFormat.MEDIUM, getLocale()); 67 | formats.put("medium", ((SimpleDateFormat) dateFormatter).toLocalizedPattern()); 68 | dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT, getLocale()); 69 | formats.put("short", ((SimpleDateFormat) dateFormatter).toLocalizedPattern()); 70 | constants.put("localeDateFormats", formats); 71 | 72 | return constants; 73 | } 74 | 75 | @ReactMethod 76 | public void decimalStyle(Double number, Promise promise) { 77 | try { 78 | NumberFormat nf = NumberFormat.getNumberInstance(getLocale()); 79 | nf.setGroupingUsed(true); 80 | promise.resolve(nf.format(number)); 81 | } catch (Exception e) { 82 | promise.reject(e.getMessage()); 83 | } 84 | } 85 | 86 | @ReactMethod 87 | public void numberFromDecimalString(String numberString, Promise promise) { 88 | try { 89 | NumberFormat nf = NumberFormat.getInstance(getLocale()); 90 | promise.resolve(nf.parse(numberString).doubleValue()); 91 | } catch (Exception e) { 92 | promise.reject(e.getMessage()); 93 | } 94 | } 95 | 96 | @ReactMethod 97 | public void dateFormat(String timestamp, String dateStyle, String timeStyle, Promise promise) { 98 | try { 99 | Date date = new Date(Long.parseLong(timestamp)); 100 | 101 | int dateStyleInt = this.getDateFormatIntFromString(dateStyle); 102 | int timeStyleInt = this.getDateFormatIntFromString(timeStyle); 103 | 104 | DateFormat dateFormatter; 105 | if(!dateStyle.equals("none") && !timeStyle.equals("none")) { 106 | dateFormatter = DateFormat.getDateTimeInstance(dateStyleInt, timeStyleInt, getLocale()); 107 | } else if(!dateStyle.equals("none") && timeStyle.equals("none")) { 108 | dateFormatter = DateFormat.getDateInstance(dateStyleInt, getLocale()); 109 | } else { 110 | dateFormatter = DateFormat.getTimeInstance(timeStyleInt, getLocale()); 111 | } 112 | 113 | promise.resolve(dateFormatter.format(date)); 114 | 115 | } catch (Exception e) { 116 | promise.reject(e.getMessage()); 117 | } 118 | } 119 | 120 | private int getDateFormatIntFromString(String stringStyle) { 121 | int dateFormatStyleint; 122 | switch(stringStyle) { 123 | case "long": 124 | dateFormatStyleint = DateFormat.LONG; 125 | break; 126 | case "medium": 127 | dateFormatStyleint = DateFormat.MEDIUM; 128 | break; 129 | case "short": 130 | dateFormatStyleint = DateFormat.SHORT; 131 | break; 132 | case "default": 133 | dateFormatStyleint = DateFormat.DEFAULT; 134 | break; 135 | default: 136 | dateFormatStyleint = DateFormat.FULL; 137 | break; 138 | } 139 | return dateFormatStyleint; 140 | } 141 | 142 | private Locale getLocale() { 143 | Locale current = mContext.getResources().getConfiguration().locale; 144 | return current; 145 | } 146 | 147 | private DecimalFormat getDecimalFormat() { 148 | DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(getLocale()); 149 | return formatter; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /ios/RCTLocale.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DB248BF51C18396500105A5D /* RCTLocale.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DB248BF41C18396500105A5D /* RCTLocale.h */; }; 11 | DB248BF71C18396500105A5D /* RCTLocale.m in Sources */ = {isa = PBXBuildFile; fileRef = DB248BF61C18396500105A5D /* RCTLocale.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | DB248BF01C18396500105A5D /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | DB248BF51C18396500105A5D /* RCTLocale.h in CopyFiles */, 22 | ); 23 | runOnlyForDeploymentPostprocessing = 0; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | DB248BF21C18396500105A5D /* libRCTLocale.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTLocale.a; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | DB248BF41C18396500105A5D /* RCTLocale.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTLocale.h; sourceTree = ""; }; 30 | DB248BF61C18396500105A5D /* RCTLocale.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTLocale.m; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | DB248BEF1C18396500105A5D /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | DB0CBAD11C1831B200168F2E = { 45 | isa = PBXGroup; 46 | children = ( 47 | DB9768661C183ADA00042D1A /* Products */, 48 | DB248BF31C18396500105A5D /* RCTLocale */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | DB248BF31C18396500105A5D /* RCTLocale */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | DB248BF41C18396500105A5D /* RCTLocale.h */, 56 | DB248BF61C18396500105A5D /* RCTLocale.m */, 57 | ); 58 | path = RCTLocale; 59 | sourceTree = ""; 60 | }; 61 | DB9768661C183ADA00042D1A /* Products */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | DB248BF21C18396500105A5D /* libRCTLocale.a */, 65 | ); 66 | name = Products; 67 | sourceTree = ""; 68 | }; 69 | /* End PBXGroup section */ 70 | 71 | /* Begin PBXNativeTarget section */ 72 | DB248BF11C18396500105A5D /* RCTLocale */ = { 73 | isa = PBXNativeTarget; 74 | buildConfigurationList = DB248BF81C18396500105A5D /* Build configuration list for PBXNativeTarget "RCTLocale" */; 75 | buildPhases = ( 76 | DB248BEE1C18396500105A5D /* Sources */, 77 | DB248BEF1C18396500105A5D /* Frameworks */, 78 | DB248BF01C18396500105A5D /* CopyFiles */, 79 | ); 80 | buildRules = ( 81 | ); 82 | dependencies = ( 83 | ); 84 | name = RCTLocale; 85 | productName = RCTLocale; 86 | productReference = DB248BF21C18396500105A5D /* libRCTLocale.a */; 87 | productType = "com.apple.product-type.library.static"; 88 | }; 89 | /* End PBXNativeTarget section */ 90 | 91 | /* Begin PBXProject section */ 92 | DB0CBAD21C1831B200168F2E /* Project object */ = { 93 | isa = PBXProject; 94 | attributes = { 95 | LastUpgradeCheck = 0710; 96 | TargetAttributes = { 97 | DB248BF11C18396500105A5D = { 98 | CreatedOnToolsVersion = 7.1.1; 99 | }; 100 | }; 101 | }; 102 | buildConfigurationList = DB0CBAD51C1831B200168F2E /* Build configuration list for PBXProject "RCTLocale" */; 103 | compatibilityVersion = "Xcode 3.2"; 104 | developmentRegion = English; 105 | hasScannedForEncodings = 0; 106 | knownRegions = ( 107 | en, 108 | ); 109 | mainGroup = DB0CBAD11C1831B200168F2E; 110 | productRefGroup = DB0CBAD11C1831B200168F2E; 111 | projectDirPath = ""; 112 | projectRoot = ""; 113 | targets = ( 114 | DB248BF11C18396500105A5D /* RCTLocale */, 115 | ); 116 | }; 117 | /* End PBXProject section */ 118 | 119 | /* Begin PBXSourcesBuildPhase section */ 120 | DB248BEE1C18396500105A5D /* Sources */ = { 121 | isa = PBXSourcesBuildPhase; 122 | buildActionMask = 2147483647; 123 | files = ( 124 | DB248BF71C18396500105A5D /* RCTLocale.m in Sources */, 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | /* End PBXSourcesBuildPhase section */ 129 | 130 | /* Begin XCBuildConfiguration section */ 131 | DB0CBAD61C1831B200168F2E /* Debug */ = { 132 | isa = XCBuildConfiguration; 133 | buildSettings = { 134 | HEADER_SEARCH_PATHS = ( 135 | "$(SRCROOT)/../../React/**", 136 | "$(SRCROOT)/../../react-native/React/**", 137 | "$(SRCROOT)/node_modules/react-native/React/**", 138 | ); 139 | }; 140 | name = Debug; 141 | }; 142 | DB0CBAD71C1831B200168F2E /* Release */ = { 143 | isa = XCBuildConfiguration; 144 | buildSettings = { 145 | HEADER_SEARCH_PATHS = ( 146 | "$(SRCROOT)/../../React/**", 147 | "$(SRCROOT)/../../react-native/React/**", 148 | "$(SRCROOT)/node_modules/react-native/React/**", 149 | ); 150 | }; 151 | name = Release; 152 | }; 153 | DB248BF91C18396500105A5D /* Debug */ = { 154 | isa = XCBuildConfiguration; 155 | buildSettings = { 156 | ALWAYS_SEARCH_USER_PATHS = NO; 157 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 158 | CLANG_CXX_LIBRARY = "libc++"; 159 | CLANG_ENABLE_MODULES = YES; 160 | CLANG_ENABLE_OBJC_ARC = YES; 161 | CLANG_WARN_BOOL_CONVERSION = YES; 162 | CLANG_WARN_CONSTANT_CONVERSION = YES; 163 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 164 | CLANG_WARN_EMPTY_BODY = YES; 165 | CLANG_WARN_ENUM_CONVERSION = YES; 166 | CLANG_WARN_INT_CONVERSION = YES; 167 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 168 | CLANG_WARN_UNREACHABLE_CODE = YES; 169 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 170 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 171 | COPY_PHASE_STRIP = NO; 172 | DEBUG_INFORMATION_FORMAT = dwarf; 173 | ENABLE_STRICT_OBJC_MSGSEND = YES; 174 | ENABLE_TESTABILITY = YES; 175 | GCC_C_LANGUAGE_STANDARD = gnu99; 176 | GCC_DYNAMIC_NO_PIC = NO; 177 | GCC_NO_COMMON_BLOCKS = YES; 178 | GCC_OPTIMIZATION_LEVEL = 0; 179 | GCC_PREPROCESSOR_DEFINITIONS = ( 180 | "DEBUG=1", 181 | "$(inherited)", 182 | ); 183 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 184 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 185 | GCC_WARN_UNDECLARED_SELECTOR = YES; 186 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 187 | GCC_WARN_UNUSED_FUNCTION = YES; 188 | GCC_WARN_UNUSED_VARIABLE = YES; 189 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 190 | MTL_ENABLE_DEBUG_INFO = YES; 191 | ONLY_ACTIVE_ARCH = YES; 192 | OTHER_LDFLAGS = "-ObjC"; 193 | PRODUCT_NAME = "$(TARGET_NAME)"; 194 | SDKROOT = iphoneos; 195 | SKIP_INSTALL = YES; 196 | }; 197 | name = Debug; 198 | }; 199 | DB248BFA1C18396500105A5D /* Release */ = { 200 | isa = XCBuildConfiguration; 201 | buildSettings = { 202 | ALWAYS_SEARCH_USER_PATHS = NO; 203 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 204 | CLANG_CXX_LIBRARY = "libc++"; 205 | CLANG_ENABLE_MODULES = YES; 206 | CLANG_ENABLE_OBJC_ARC = YES; 207 | CLANG_WARN_BOOL_CONVERSION = YES; 208 | CLANG_WARN_CONSTANT_CONVERSION = YES; 209 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 210 | CLANG_WARN_EMPTY_BODY = YES; 211 | CLANG_WARN_ENUM_CONVERSION = YES; 212 | CLANG_WARN_INT_CONVERSION = YES; 213 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 214 | CLANG_WARN_UNREACHABLE_CODE = YES; 215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 216 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 217 | COPY_PHASE_STRIP = NO; 218 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 219 | ENABLE_NS_ASSERTIONS = NO; 220 | ENABLE_STRICT_OBJC_MSGSEND = YES; 221 | GCC_C_LANGUAGE_STANDARD = gnu99; 222 | GCC_NO_COMMON_BLOCKS = YES; 223 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 224 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 225 | GCC_WARN_UNDECLARED_SELECTOR = YES; 226 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 227 | GCC_WARN_UNUSED_FUNCTION = YES; 228 | GCC_WARN_UNUSED_VARIABLE = YES; 229 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 230 | MTL_ENABLE_DEBUG_INFO = NO; 231 | OTHER_LDFLAGS = "-ObjC"; 232 | PRODUCT_NAME = "$(TARGET_NAME)"; 233 | SDKROOT = iphoneos; 234 | SKIP_INSTALL = YES; 235 | VALIDATE_PRODUCT = YES; 236 | }; 237 | name = Release; 238 | }; 239 | /* End XCBuildConfiguration section */ 240 | 241 | /* Begin XCConfigurationList section */ 242 | DB0CBAD51C1831B200168F2E /* Build configuration list for PBXProject "RCTLocale" */ = { 243 | isa = XCConfigurationList; 244 | buildConfigurations = ( 245 | DB0CBAD61C1831B200168F2E /* Debug */, 246 | DB0CBAD71C1831B200168F2E /* Release */, 247 | ); 248 | defaultConfigurationIsVisible = 0; 249 | defaultConfigurationName = Release; 250 | }; 251 | DB248BF81C18396500105A5D /* Build configuration list for PBXNativeTarget "RCTLocale" */ = { 252 | isa = XCConfigurationList; 253 | buildConfigurations = ( 254 | DB248BF91C18396500105A5D /* Debug */, 255 | DB248BFA1C18396500105A5D /* Release */, 256 | ); 257 | defaultConfigurationIsVisible = 0; 258 | defaultConfigurationName = Release; 259 | }; 260 | /* End XCConfigurationList section */ 261 | }; 262 | rootObject = DB0CBAD21C1831B200168F2E /* Project object */; 263 | } 264 | --------------------------------------------------------------------------------